datagrok-tools 6.7.0 → 6.7.2
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/.devcontainer/entrypoint.sh +0 -0
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +8 -1
- package/CLAUDE.md +5 -6
- package/LICENSE.md +16 -0
- package/README.md +5 -3
- package/bin/commands/build.js +123 -139
- package/bin/commands/check.js +3 -4
- package/bin/commands/create.js +37 -11
- package/bin/commands/help.js +59 -17
- package/bin/commands/link.js +10 -0
- package/bin/commands/publish.js +36 -13
- package/bin/commands/setup.js +139 -0
- package/bin/commands/tsc.js +26 -0
- package/bin/grok.js +34 -21
- package/bin/utils/migrate/bundle.js +6 -2
- package/bin/utils/migrate/bundle.ts +5 -2
- package/bin/utils/migrate/pusher.js +8 -1
- package/bin/utils/migrate/pusher.ts +7 -1
- package/bin/utils/toolchain.js +121 -0
- package/bin/utils/utils.js +9 -0
- package/package-template/.vscode/tasks.json +1 -1
- package/package-template/package.json +9 -14
- package/package-template/tsconfig.json +2 -69
- package/package.json +17 -17
- package/plugins/func-gen-plugin.js +9 -4
- package/turbo.json +9 -0
- package/package-template/.eslintrc.json +0 -38
- package/package-template/ts.webpack.config.js +0 -46
- package/package-template/webpack.config.js +0 -35
package/bin/commands/help.js
CHANGED
|
@@ -13,8 +13,10 @@ Datagrok's package management tool
|
|
|
13
13
|
Commands:
|
|
14
14
|
add Add an object template
|
|
15
15
|
api Create wrapper functions
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
setup Get a public/ checkout ready to build (pnpm, install, npm-era clean-up)
|
|
17
|
+
build Build a package and its dependencies (Turborepo), or just this package
|
|
18
|
+
tsc Run the workspace TypeScript compiler
|
|
19
|
+
check Check package content (function signatures, etc.)
|
|
18
20
|
claude Launch Claude Code in a Datagrok dev container
|
|
19
21
|
config Create and manage config files
|
|
20
22
|
create Create a package
|
|
@@ -209,7 +211,8 @@ Options:
|
|
|
209
211
|
--all Publish all available packages (run in packages directory)
|
|
210
212
|
--refresh Publish all available already loaded packages (run in packages directory)
|
|
211
213
|
--link Link the package to local packages
|
|
212
|
-
--build Builds the package
|
|
214
|
+
--build Builds the package (the default; kept for compatibility)
|
|
215
|
+
--skip-build Upload the existing dist/ without rebuilding
|
|
213
216
|
--release Publish package as release version
|
|
214
217
|
--rebuild-docker Force rebuild Docker images locally before pushing to registry
|
|
215
218
|
--skip-docker-rebuild Skip auto-rebuild when Dockerfile folder has changed
|
|
@@ -308,6 +311,9 @@ https://datagrok.ai/help/develop/how-to/test-packages#local-testing
|
|
|
308
311
|
const HELP_LINK = `
|
|
309
312
|
Usage: grok link
|
|
310
313
|
|
|
314
|
+
In a pnpm workspace checkout (public/) this is a no-op: \`pnpm install\` at the repository root
|
|
315
|
+
links every in-repo dependency (workspace:^). The options below apply to standalone checkouts only.
|
|
316
|
+
|
|
311
317
|
Links \`datagrok-api\`, all necessary libraries and packages for local development.
|
|
312
318
|
Uses \`npm link\` unless the --path option specified.
|
|
313
319
|
By default, it links packages from the parent directory of the repository's root.
|
|
@@ -341,27 +347,61 @@ Example:
|
|
|
341
347
|
⟶
|
|
342
348
|
meta: { role: 'viewer,ml' }
|
|
343
349
|
`;
|
|
350
|
+
const HELP_SETUP = `
|
|
351
|
+
Usage: grok setup [--check] [--global]
|
|
352
|
+
|
|
353
|
+
Gets a public/ checkout (or a fresh worktree) ready to build, and keeps it that way after a pull:
|
|
354
|
+
1. Node 20+ (22 recommended); pnpm through corepack, at the version the workspace pins
|
|
355
|
+
2. removes per-package node_modules left by the npm era and stray package-lock.json files
|
|
356
|
+
3. pnpm install at the workspace root
|
|
357
|
+
4. reports a global grok older than the workspace one
|
|
358
|
+
|
|
359
|
+
--check Report only, change nothing
|
|
360
|
+
--global Also update the global datagrok-tools to the workspace version
|
|
361
|
+
|
|
362
|
+
Examples:
|
|
363
|
+
grok setup After cloning, after a pull that changed dependencies, in a new worktree
|
|
364
|
+
grok setup --check What would change
|
|
365
|
+
`;
|
|
366
|
+
const HELP_TSC = `
|
|
367
|
+
Usage: grok tsc [tsc arguments]
|
|
368
|
+
|
|
369
|
+
Runs the workspace TypeScript compiler (the version @datagrok/build-config pins) with the given arguments,
|
|
370
|
+
so a package never needs its own tsc. After an emitting run in a library, the css/wasm/json assets its
|
|
371
|
+
sources import are mirrored into dist/.
|
|
372
|
+
|
|
373
|
+
Examples:
|
|
374
|
+
grok tsc --noEmit -p tsconfig.json Type-check (what the \`typecheck\` script runs)
|
|
375
|
+
grok tsc -p tsconfig.build.json A library with a custom build config
|
|
376
|
+
`;
|
|
344
377
|
const HELP_BUILD = `
|
|
345
378
|
Usage: grok build
|
|
346
379
|
|
|
347
|
-
|
|
380
|
+
Inside the public/ workspace: build the package in the current directory and everything it depends
|
|
381
|
+
on, in dependency order, through Turborepo (cached: unchanged packages take milliseconds). Never prompts.
|
|
382
|
+
Inside a Turborepo task, in a standalone package, or with --local: build just this package — an rspack
|
|
383
|
+
bundle (src/package.ts -> dist/package.js, function metadata generated, \`grok check --soft\`) for a
|
|
384
|
+
plugin, a TypeScript emit into dist/ for a library.
|
|
348
385
|
|
|
349
386
|
Options:
|
|
350
|
-
[
|
|
351
|
-
|
|
352
|
-
--
|
|
353
|
-
--
|
|
354
|
-
--
|
|
355
|
-
--
|
|
356
|
-
--parallel N Max parallel builds (default:
|
|
357
|
-
--
|
|
387
|
+
[--all] [--affected] [--typecheck] [--filter <turbo filter>] [--parallel N] [--force] [--local] [--skip-check] [-v | --verbose]
|
|
388
|
+
|
|
389
|
+
--all Build the whole workspace
|
|
390
|
+
--affected Build everything the diff against origin/master affects, dependents included
|
|
391
|
+
--typecheck Also run the type-check task
|
|
392
|
+
--filter Extra Turborepo filter (e.g. --filter "@datagrok/chem...")
|
|
393
|
+
--parallel N Max parallel builds (default: 3)
|
|
394
|
+
--force Ignore the build cache
|
|
395
|
+
--local Build only this package, without Turborepo
|
|
396
|
+
--skip-check Local build: skip \`grok check\`
|
|
397
|
+
--verbose Show full build output instead of errors only
|
|
358
398
|
|
|
359
399
|
Examples:
|
|
360
|
-
grok build
|
|
361
|
-
grok build
|
|
362
|
-
grok build
|
|
363
|
-
grok build
|
|
364
|
-
grok build
|
|
400
|
+
grok build This package and its dependencies
|
|
401
|
+
grok build --all Everything
|
|
402
|
+
grok build --affected What my change touches (CI does the same)
|
|
403
|
+
grok build --typecheck Bundle and type-check
|
|
404
|
+
grok build --local Just this package (what the package's build script runs)
|
|
365
405
|
`;
|
|
366
406
|
|
|
367
407
|
// const HELP_MIGRATE = `
|
|
@@ -440,9 +480,11 @@ const help = exports.help = {
|
|
|
440
480
|
report: HELP_REPORT,
|
|
441
481
|
run: HELP_RUN,
|
|
442
482
|
test: HELP_TEST,
|
|
483
|
+
tsc: HELP_TSC,
|
|
443
484
|
testall: HELP_TESTALL,
|
|
444
485
|
migrate: HELP_MIGRATE,
|
|
445
486
|
server: _server.HELP_SERVER,
|
|
446
487
|
s: _server.HELP_SERVER,
|
|
488
|
+
setup: HELP_SETUP,
|
|
447
489
|
help: HELP
|
|
448
490
|
};
|
package/bin/commands/link.js
CHANGED
|
@@ -30,11 +30,21 @@ while (_path.default.dirname(dirStep) !== dirStep) {
|
|
|
30
30
|
}
|
|
31
31
|
dirStep = _path.default.dirname(dirStep);
|
|
32
32
|
}
|
|
33
|
+
function isPnpmWorkspace(dir) {
|
|
34
|
+
for (let d = _path.default.resolve(dir);; d = _path.default.dirname(d)) {
|
|
35
|
+
if (_fs.default.existsSync(_path.default.join(d, 'pnpm-workspace.yaml'))) return true;
|
|
36
|
+
if (_path.default.dirname(d) === d) return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
33
39
|
let verbose = false;
|
|
34
40
|
let pathMode = false;
|
|
35
41
|
let devMode = false;
|
|
36
42
|
let unlink = false;
|
|
37
43
|
async function link(args) {
|
|
44
|
+
if (isPnpmWorkspace(curDir)) {
|
|
45
|
+
console.log('This checkout is a pnpm workspace: in-repo dependencies are linked by `pnpm install` ' + '(workspace:^), and `grok link` is not needed. Run `pnpm install` at the repository root.');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
38
48
|
verbose = args.verbose ?? false;
|
|
39
49
|
devMode = args.dev ?? false;
|
|
40
50
|
pathMode = args.path ?? false;
|
package/bin/commands/publish.js
CHANGED
|
@@ -4,6 +4,8 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
+
exports.BUNDLE_MARKER_NAME = exports.BUNDLE_MARKER = void 0;
|
|
8
|
+
exports.predatesBundleDetection = predatesBundleDetection;
|
|
7
9
|
exports.processPackage = processPackage;
|
|
8
10
|
exports.publish = publish;
|
|
9
11
|
var _archiver = _interopRequireDefault(require("archiver"));
|
|
@@ -487,6 +489,21 @@ async function fallbackImage(img, host, devKey, registry, version, contentHash)
|
|
|
487
489
|
requestedVersion: img.imageTag
|
|
488
490
|
};
|
|
489
491
|
}
|
|
492
|
+
const BUNDLE_MARKER_NAME = exports.BUNDLE_MARKER_NAME = 'webpack.config.js';
|
|
493
|
+
const BUNDLE_MARKER = exports.BUNDLE_MARKER = '// Generated by `grok publish` for servers before 1.28.0, which detect a ' + 'bundled\n// package by this file. The bundle itself is built by @datagrok/build-config.\nmodule.exports = {};\n';
|
|
494
|
+
function predatesBundleDetection(version) {
|
|
495
|
+
const [major, minor, patch] = (version ?? '').split('.').map(Number);
|
|
496
|
+
if ([major, minor, patch].some(isNaN)) return true;
|
|
497
|
+
return major < 1 || major === 1 && minor < 28;
|
|
498
|
+
}
|
|
499
|
+
async function serverPredatesBundleDetection(host) {
|
|
500
|
+
try {
|
|
501
|
+
const resp = await (0, _nodeFetch.default)(`${host}/info/server`);
|
|
502
|
+
return predatesBundleDetection((await resp.json())?.Version);
|
|
503
|
+
} catch {
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
490
507
|
async function processPackage(debug, rebuild, host, devKey, packageName, dropDb, suffix, hostAlias, registry, rebuildDocker, skipDockerRebuild) {
|
|
491
508
|
// Validate server connectivity and dev key
|
|
492
509
|
let timestamps = {};
|
|
@@ -526,7 +543,10 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
526
543
|
includeEmpty: false,
|
|
527
544
|
follow: true
|
|
528
545
|
});
|
|
529
|
-
|
|
546
|
+
|
|
547
|
+
// A bundled package ships dist/package.js: built by webpack (legacy) or by `grok build`
|
|
548
|
+
// (rspack via @datagrok/build-config, which needs no config file at all).
|
|
549
|
+
const isWebpack = _fs.default.existsSync('webpack.config.js') || _fs.default.existsSync('rspack.config.js') || _fs.default.existsSync('dist/package.js') || _fs.default.existsSync(_path.default.join(curDir, 'src', 'package.ts'));
|
|
530
550
|
if (!rebuild && isWebpack) {
|
|
531
551
|
if (_fs.default.existsSync('dist/package.js')) {
|
|
532
552
|
const distFiles = await (0, _ignoreWalk.default)({
|
|
@@ -550,12 +570,6 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
550
570
|
const json = JSON.parse(_fs.default.readFileSync(packageFilePath, {
|
|
551
571
|
encoding: 'utf-8'
|
|
552
572
|
}));
|
|
553
|
-
if (isWebpack) {
|
|
554
|
-
const webpackConfigPath = _path.default.join(curDir, 'webpack.config.js');
|
|
555
|
-
const content = _fs.default.readFileSync(webpackConfigPath, {
|
|
556
|
-
encoding: 'utf-8'
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
573
|
const funcFiles = jsTsFiles.filter(f => packageFiles.includes(f));
|
|
560
574
|
color.log(`Checks finished in ${Date.now() - checkStart} ms`);
|
|
561
575
|
const reg = new RegExp(/\${(\w*)}/g);
|
|
@@ -615,6 +629,13 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
615
629
|
if (userInfo) dockerVersion = userInfo.login;
|
|
616
630
|
}
|
|
617
631
|
await processDockerImages(packageName, dockerVersion, registry, devKey, host, rebuildDocker ?? false, zip, localTimestamps, debug, skipDockerRebuild ?? false, generatedDockerDirs);
|
|
632
|
+
const bundledWithoutMarker = !rebuild && _fs.default.existsSync('dist/package.js') && !_fs.default.existsSync(BUNDLE_MARKER_NAME);
|
|
633
|
+
if (bundledWithoutMarker && (await serverPredatesBundleDetection(host))) {
|
|
634
|
+
zip.append(BUNDLE_MARKER, {
|
|
635
|
+
name: BUNDLE_MARKER_NAME
|
|
636
|
+
});
|
|
637
|
+
localTimestamps[BUNDLE_MARKER_NAME] = new Date().toUTCString();
|
|
638
|
+
}
|
|
618
639
|
zip.append(JSON.stringify(localTimestamps), {
|
|
619
640
|
name: 'timestamps.json'
|
|
620
641
|
});
|
|
@@ -692,12 +713,14 @@ async function publish(args) {
|
|
|
692
713
|
}
|
|
693
714
|
async function publishPackage(args) {
|
|
694
715
|
const nArgs = args['_'].length;
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
716
|
+
|
|
717
|
+
// A bundled package is built locally before upload unless --skip-build. Inside the pnpm
|
|
718
|
+
// workspace the install already happened at the root; standalone packages install first.
|
|
719
|
+
if (!args.link && !args['skip-build'] && !args.rebuild && _fs.default.existsSync(_path.default.join(curDir, 'src'))) {
|
|
720
|
+
color.log('Building');
|
|
721
|
+
const workspace = utils.isPnpmWorkspace(curDir);
|
|
722
|
+
if (!workspace && !_fs.default.existsSync(_path.default.join(curDir, 'node_modules'))) await utils.runScript('npm install', curDir, false);
|
|
723
|
+
await utils.runScript(workspace ? 'pnpm run build' : 'npm run build', curDir, false);
|
|
701
724
|
}
|
|
702
725
|
if (args.debug && args.release) {
|
|
703
726
|
color.error('Incompatible options: --debug and --release');
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.setup = setup;
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
9
|
+
var _path = _interopRequireDefault(require("path"));
|
|
10
|
+
var _child_process = require("child_process");
|
|
11
|
+
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
12
|
+
var _build = require("./build");
|
|
13
|
+
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); }
|
|
14
|
+
/**
|
|
15
|
+
* `grok setup`: one command to get (or keep) a public/ checkout ready to build.
|
|
16
|
+
* 1. Node 20+ (22 recommended); pnpm through corepack, at the version package.json pins.
|
|
17
|
+
* 2. Removes per-package node_modules left by the npm era and stray package-lock.json files.
|
|
18
|
+
* 3. `pnpm install` at the workspace root.
|
|
19
|
+
* 4. Reports a global `grok` older than the workspace one (`--global` updates it).
|
|
20
|
+
* `--check` only reports.
|
|
21
|
+
*/
|
|
22
|
+
async function setup(args) {
|
|
23
|
+
const root = (0, _build.findWorkspaceRoot)(process.cwd());
|
|
24
|
+
if (!root) {
|
|
25
|
+
color.error('Not inside the public/ workspace (no pnpm-workspace.yaml above the current directory).');
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const check = !!args.check;
|
|
29
|
+
const rootPkg = JSON.parse(_fs.default.readFileSync(_path.default.join(root, 'package.json'), 'utf8'));
|
|
30
|
+
const pinnedPnpm = (rootPkg.packageManager || 'pnpm@10').split('@')[1];
|
|
31
|
+
let ok = true;
|
|
32
|
+
|
|
33
|
+
// 1. Node and pnpm
|
|
34
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
35
|
+
if (nodeMajor < 20) {
|
|
36
|
+
color.error(`Node ${process.versions.node}: 20 or later is required (22 recommended).`);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
color.info(`Node ${process.versions.node}`);
|
|
40
|
+
let pnpmVersion = run('pnpm', ['--version']);
|
|
41
|
+
if (pnpmVersion !== pinnedPnpm) {
|
|
42
|
+
color.warn(`pnpm ${pnpmVersion || 'not found'}; the workspace pins ${pinnedPnpm}.`);
|
|
43
|
+
if (!check) {
|
|
44
|
+
if (run('corepack', ['--version']) === null) color.warn('corepack not found: `npm install -g corepack`, then rerun `grok setup`.');else {
|
|
45
|
+
(0, _child_process.spawnSync)('corepack', ['enable'], {
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
shell: true
|
|
48
|
+
});
|
|
49
|
+
(0, _child_process.spawnSync)('corepack', ['prepare', `pnpm@${pinnedPnpm}`, '--activate'], {
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
shell: true
|
|
52
|
+
});
|
|
53
|
+
pnpmVersion = run('pnpm', ['--version']);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (pnpmVersion === pinnedPnpm) color.info(`pnpm ${pnpmVersion}`);else ok = false;
|
|
58
|
+
|
|
59
|
+
// 2. leftovers from the npm era
|
|
60
|
+
const projectDirs = ['js-api', 'tools', 'build-config'].map(d => _path.default.join(root, d));
|
|
61
|
+
for (const group of ['packages', 'libraries']) for (const d of _fs.default.readdirSync(_path.default.join(root, group))) projectDirs.push(_path.default.join(root, group, d));
|
|
62
|
+
const staleModules = projectDirs.filter(d => isLegacyNodeModules(_path.default.join(d, 'node_modules')));
|
|
63
|
+
const staleLocks = projectDirs.map(d => _path.default.join(d, 'package-lock.json')).filter(f => _fs.default.existsSync(f));
|
|
64
|
+
if (staleModules.length || staleLocks.length) {
|
|
65
|
+
color.warn(`${staleModules.length} per-package node_modules from npm and ${staleLocks.length} package-lock.json file(s)` + (check ? ' would be removed' : ': removing'));
|
|
66
|
+
if (!check) {
|
|
67
|
+
for (const d of staleModules) _fs.default.rmSync(_path.default.join(d, 'node_modules'), {
|
|
68
|
+
recursive: true,
|
|
69
|
+
force: true
|
|
70
|
+
});
|
|
71
|
+
for (const f of staleLocks) _fs.default.rmSync(f, {
|
|
72
|
+
force: true
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
} else color.info('no npm-era leftovers');
|
|
76
|
+
|
|
77
|
+
// 3. install
|
|
78
|
+
if (!check && pnpmVersion === pinnedPnpm) {
|
|
79
|
+
color.log('pnpm install ...');
|
|
80
|
+
const r = (0, _child_process.spawnSync)('pnpm', ['install', '--frozen-lockfile'], {
|
|
81
|
+
stdio: 'inherit',
|
|
82
|
+
cwd: root,
|
|
83
|
+
shell: true
|
|
84
|
+
});
|
|
85
|
+
if (r.status !== 0) {
|
|
86
|
+
color.warn('frozen install failed (lockfile out of date?): retrying without --frozen-lockfile');
|
|
87
|
+
if ((0, _child_process.spawnSync)('pnpm', ['install'], {
|
|
88
|
+
stdio: 'inherit',
|
|
89
|
+
cwd: root,
|
|
90
|
+
shell: true
|
|
91
|
+
}).status !== 0) {
|
|
92
|
+
color.error('pnpm install failed');
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 4. the global grok
|
|
99
|
+
const workspaceGrok = JSON.parse(_fs.default.readFileSync(_path.default.join(root, 'tools', 'package.json'), 'utf8')).version;
|
|
100
|
+
// datagrok-tools before 6.6 has no --version and prints its usage instead
|
|
101
|
+
const raw = run('grok', ['--version']);
|
|
102
|
+
const globalGrok = raw === null ? null : /^\d+\.\d+\.\d+/.test(raw) ? raw : 'older than 6.6';
|
|
103
|
+
if (globalGrok && globalGrok !== workspaceGrok) {
|
|
104
|
+
color.warn(`global grok ${globalGrok}; the workspace has ${workspaceGrok}` + (args.global && !check ? ': updating' : ' (run `npm install -g datagrok-tools@' + workspaceGrok + '`, or `grok setup --global`)'));
|
|
105
|
+
if (args.global && !check) (0, _child_process.spawnSync)('npm', ['install', '-g', `datagrok-tools@${workspaceGrok}`], {
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
shell: true
|
|
108
|
+
});
|
|
109
|
+
} else if (globalGrok) color.info(`grok ${globalGrok}`);
|
|
110
|
+
if (ok && !check) color.success('Ready: `grok build` in any package, `grok build --all` for everything.');
|
|
111
|
+
return ok;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** npm's node_modules holds real directories; pnpm's per-package one holds only symlinks (and .bin). */
|
|
115
|
+
function isLegacyNodeModules(dir) {
|
|
116
|
+
if (!_fs.default.existsSync(dir)) return false;
|
|
117
|
+
for (const e of _fs.default.readdirSync(dir, {
|
|
118
|
+
withFileTypes: true
|
|
119
|
+
})) {
|
|
120
|
+
if (e.name === '.bin' || e.name === '.cache' || e.name.startsWith('.')) continue;
|
|
121
|
+
const p = _path.default.join(dir, e.name);
|
|
122
|
+
if (e.isSymbolicLink()) continue;
|
|
123
|
+
if (e.isDirectory() && e.name.startsWith('@')) {
|
|
124
|
+
if (_fs.default.readdirSync(p, {
|
|
125
|
+
withFileTypes: true
|
|
126
|
+
}).some(s => s.isDirectory() && !s.isSymbolicLink())) return true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (e.isDirectory()) return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function run(cmd, args) {
|
|
134
|
+
const r = (0, _child_process.spawnSync)(cmd, args, {
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
shell: true
|
|
137
|
+
});
|
|
138
|
+
return r.status === 0 ? r.stdout.trim().split(/\r?\n/).pop() || '' : null;
|
|
139
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.tsc = tsc;
|
|
7
|
+
var _child_process = require("child_process");
|
|
8
|
+
var _toolchain = require("../utils/toolchain");
|
|
9
|
+
/**
|
|
10
|
+
* `grok tsc ...` runs the workspace TypeScript compiler (the one `@datagrok/build-config` pins) with the
|
|
11
|
+
* given arguments, so package scripts never depend on a locally installed `tsc`. After an emitting run
|
|
12
|
+
* in a library, the assets its sources import are mirrored into dist/.
|
|
13
|
+
*/
|
|
14
|
+
async function tsc(args) {
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
const extra = process.argv.slice(process.argv.indexOf('tsc') + 1);
|
|
17
|
+
const emitting = !extra.includes('--noEmit');
|
|
18
|
+
if (emitting) (0, _toolchain.ensureLibraryExports)(cwd);
|
|
19
|
+
const r = (0, _child_process.spawnSync)(process.execPath, [(0, _toolchain.toolchain)(cwd).tsc, ...extra], {
|
|
20
|
+
stdio: 'inherit',
|
|
21
|
+
cwd
|
|
22
|
+
});
|
|
23
|
+
if (r.status !== 0) return false;
|
|
24
|
+
if (emitting) (0, _toolchain.copyLibraryAssets)(cwd);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
package/bin/grok.js
CHANGED
|
@@ -9,32 +9,45 @@ const argv = require('minimist')(process.argv.slice(2), {
|
|
|
9
9
|
// test.ts / playwright-runner.ts never fired and `--no-retry` was silently ignored
|
|
10
10
|
// (Playwright kept retrying failed specs). Normalize back to the flag the commands read.
|
|
11
11
|
if (argv.retry === false) argv['no-retry'] = true;
|
|
12
|
-
|
|
12
|
+
// The help texts are a large module; load them only when one is printed.
|
|
13
|
+
let _help;
|
|
14
|
+
const help = new Proxy({}, {get: (_, key) => (_help ??= require('./commands/help').help)[key]});
|
|
13
15
|
const runAllCommand = require('./utils/utils').runAll;
|
|
14
16
|
|
|
17
|
+
// Each command module is loaded only when invoked: loading all of them (puppeteer, ts-morph,
|
|
18
|
+
// archiver, inquirer) used to cost ~6 s of start-up on every `grok api` / `grok check`.
|
|
19
|
+
const lazy = (file, name) => (args) => require(`./commands/${file}`)[name](args);
|
|
15
20
|
const commands = {
|
|
16
|
-
add:
|
|
17
|
-
api:
|
|
18
|
-
build:
|
|
19
|
-
check:
|
|
20
|
-
claude:
|
|
21
|
-
config:
|
|
22
|
-
create:
|
|
23
|
-
'docker-gen':
|
|
24
|
-
init:
|
|
25
|
-
link:
|
|
26
|
-
login:
|
|
27
|
-
publish:
|
|
28
|
-
report:
|
|
29
|
-
run:
|
|
30
|
-
test:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
add: lazy('add', 'add'),
|
|
22
|
+
api: lazy('api', 'api'),
|
|
23
|
+
build: lazy('build', 'build'),
|
|
24
|
+
check: lazy('check', 'check'),
|
|
25
|
+
claude: lazy('claude', 'claude'),
|
|
26
|
+
config: lazy('config', 'config'),
|
|
27
|
+
create: lazy('create', 'create'),
|
|
28
|
+
'docker-gen': lazy('docker-gen', 'dockerGen'),
|
|
29
|
+
init: lazy('init', 'init'),
|
|
30
|
+
link: lazy('link', 'link'),
|
|
31
|
+
login: lazy('login', 'login'),
|
|
32
|
+
publish: lazy('publish', 'publish'),
|
|
33
|
+
report: lazy('report', 'report'),
|
|
34
|
+
run: lazy('run', 'run'),
|
|
35
|
+
test: lazy('test', 'test'),
|
|
36
|
+
tsc: lazy('tsc', 'tsc'),
|
|
37
|
+
testall: lazy('test-all', 'testAll'),
|
|
38
|
+
stresstest: lazy('stress-tests', 'stressTests'),
|
|
39
|
+
migrate: lazy('migrate', 'migrate'),
|
|
40
|
+
server: lazy('server', 'server'),
|
|
41
|
+
s: lazy('server', 'server'),
|
|
42
|
+
setup: lazy('setup', 'setup'),
|
|
36
43
|
};
|
|
37
44
|
|
|
45
|
+
// `--version` is a string option (grok publish --version 1.10), so a bare `grok --version` parses as ''.
|
|
46
|
+
if (argv._.length === 0 && ('version' in argv && argv.version === '' || argv.v === true)) {
|
|
47
|
+
console.log(require('../package.json').version);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
const onPackageCommandNames = ['api', 'check', 'link', 'publish', 'test'];
|
|
39
52
|
|
|
40
53
|
// A machine-readable run prints its error as JSON on stderr (server.ts) and nothing else:
|
|
@@ -35,8 +35,12 @@ function normalize(type, json) {
|
|
|
35
35
|
if (copy.package) copy.package = {
|
|
36
36
|
id: copy.package.id
|
|
37
37
|
};
|
|
38
|
-
// The
|
|
39
|
-
if (copy.metaParams)
|
|
38
|
+
// The stamps are written by the pusher, so they must not make an unchanged entity look different.
|
|
39
|
+
if (copy.metaParams) {
|
|
40
|
+
delete copy.metaParams.sync_id;
|
|
41
|
+
delete copy.metaParams.migrated_from;
|
|
42
|
+
delete copy.metaParams.migrated_on;
|
|
43
|
+
}
|
|
40
44
|
_registry.TYPES[type]?.strip?.(copy);
|
|
41
45
|
return sortKeys(copy);
|
|
42
46
|
}
|
|
@@ -43,9 +43,12 @@ export function normalize(type: string, json: any): any {
|
|
|
43
43
|
delete copy[k];
|
|
44
44
|
if (copy.package)
|
|
45
45
|
copy.package = {id: copy.package.id};
|
|
46
|
-
// The
|
|
47
|
-
if (copy.metaParams)
|
|
46
|
+
// The stamps are written by the pusher, so they must not make an unchanged entity look different.
|
|
47
|
+
if (copy.metaParams) {
|
|
48
48
|
delete copy.metaParams.sync_id;
|
|
49
|
+
delete copy.metaParams.migrated_from;
|
|
50
|
+
delete copy.metaParams.migrated_on;
|
|
51
|
+
}
|
|
49
52
|
TYPES[type]?.strip?.(copy);
|
|
50
53
|
return sortKeys(copy);
|
|
51
54
|
}
|
|
@@ -433,7 +433,14 @@ async function saveOne(dapi, bundle, op, rows, idmap, deferred, missing) {
|
|
|
433
433
|
const spec = _registry.TYPES[op.type];
|
|
434
434
|
const payload = (0, _bundle.stripPrivate)(JSON.parse(JSON.stringify(op.json)));
|
|
435
435
|
const targetId = payload.id ?? op.id;
|
|
436
|
-
if (payload.metaParams && typeof payload.metaParams === 'object')
|
|
436
|
+
if (payload.metaParams && typeof payload.metaParams === 'object') {
|
|
437
|
+
payload.metaParams.sync_id = op.id;
|
|
438
|
+
const from = bundle.manifest?.source?.url;
|
|
439
|
+
if (from) {
|
|
440
|
+
payload.metaParams.migrated_from = from;
|
|
441
|
+
payload.metaParams.migrated_on = new Date().toISOString();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
437
444
|
|
|
438
445
|
// The server encrypts and masks password-class parameters itself, so target-side
|
|
439
446
|
// secrets go in as plain parameters and never touch the bundle.
|
|
@@ -378,8 +378,14 @@ async function saveOne(dapi: NodeDapi, bundle: Bundle, op: Op, rows: Row[], idma
|
|
|
378
378
|
const spec = TYPES[op.type];
|
|
379
379
|
const payload = stripPrivate(JSON.parse(JSON.stringify(op.json)));
|
|
380
380
|
const targetId = payload.id ?? op.id;
|
|
381
|
-
if (payload.metaParams && typeof payload.metaParams === 'object')
|
|
381
|
+
if (payload.metaParams && typeof payload.metaParams === 'object') {
|
|
382
382
|
payload.metaParams.sync_id = op.id;
|
|
383
|
+
const from = bundle.manifest?.source?.url;
|
|
384
|
+
if (from) {
|
|
385
|
+
payload.metaParams.migrated_from = from;
|
|
386
|
+
payload.metaParams.migrated_on = new Date().toISOString();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
383
389
|
|
|
384
390
|
// The server encrypts and masks password-class parameters itself, so target-side
|
|
385
391
|
// secrets go in as plain parameters and never touch the bundle.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.copyLibraryAssets = copyLibraryAssets;
|
|
8
|
+
exports.ensureLibraryExports = ensureLibraryExports;
|
|
9
|
+
exports.toolchain = toolchain;
|
|
10
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
11
|
+
var _path = _interopRequireDefault(require("path"));
|
|
12
|
+
/**
|
|
13
|
+
* The shared toolchain (`@datagrok/build-config`: rspack factory, TypeScript) as seen from `dir`:
|
|
14
|
+
* the workspace root's copy inside public/, the package's own devDependency in a standalone package.
|
|
15
|
+
*/
|
|
16
|
+
function toolchain(dir) {
|
|
17
|
+
let pkgJson;
|
|
18
|
+
try {
|
|
19
|
+
pkgJson = require.resolve('@datagrok/build-config/package.json', {
|
|
20
|
+
paths: [dir]
|
|
21
|
+
});
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error('@datagrok/build-config is not installed: run `pnpm install` at the root of public/, ' + 'or add it as a devDependency of a standalone package');
|
|
24
|
+
}
|
|
25
|
+
const buildConfigDir = _path.default.dirname(pkgJson);
|
|
26
|
+
const tsPkg = require.resolve('typescript/package.json', {
|
|
27
|
+
paths: [buildConfigDir]
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
buildConfig: require(buildConfigDir),
|
|
31
|
+
buildConfigDir,
|
|
32
|
+
tsc: _path.default.join(_path.default.dirname(tsPkg), 'bin', 'tsc')
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const ASSET = /\.(css|scss|wasm|js|mjs|cjs|json|png|jpe?g|gif|svg|txt|csv|md|html|sdf|mol)$/i;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A library's dist/ must be self-contained: the css, wasm, json and plain-js files its sources
|
|
39
|
+
* import relative to themselves are mirrored next to the compiled output. A .js next to a .ts of
|
|
40
|
+
* the same name is a stale in-place compile, not an asset.
|
|
41
|
+
*/
|
|
42
|
+
function copyLibraryAssets(dir) {
|
|
43
|
+
if (!_fs.default.existsSync(_path.default.join(dir, 'dist'))) return 0;
|
|
44
|
+
let n = 0;
|
|
45
|
+
const walk = rel => {
|
|
46
|
+
for (const e of _fs.default.readdirSync(_path.default.join(dir, rel), {
|
|
47
|
+
withFileTypes: true
|
|
48
|
+
})) {
|
|
49
|
+
if (e.name === 'node_modules' || e.name === 'dist' || e.name.startsWith('.')) continue;
|
|
50
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
51
|
+
if (e.isDirectory()) walk(r);else if (rel && ASSET.test(e.name) && e.name !== 'package.json' && !isCompiledSibling(dir, r)) {
|
|
52
|
+
const to = _path.default.join(dir, 'dist', r);
|
|
53
|
+
_fs.default.mkdirSync(_path.default.dirname(to), {
|
|
54
|
+
recursive: true
|
|
55
|
+
});
|
|
56
|
+
const from = _path.default.join(dir, r);
|
|
57
|
+
if (!_fs.default.existsSync(to) || _fs.default.statSync(to).mtimeMs < _fs.default.statSync(from).mtimeMs) {
|
|
58
|
+
_fs.default.copyFileSync(from, to);
|
|
59
|
+
n++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
walk('');
|
|
65
|
+
if (n) console.log(`copied ${n} asset file(s) into dist/`);
|
|
66
|
+
return n;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Keeps a library's package.json `exports` in step with its source tree. A library that ships dist/
|
|
71
|
+
* has an `exports` map ending in the `./*` wildcard; the wildcard cannot resolve a directory import
|
|
72
|
+
* (`@datagrok-libraries/bio/src/trees`), so every directory with an index.ts needs an explicit entry.
|
|
73
|
+
* Adds the missing ones and leaves everything else (hand-written entries, the wildcards, entries for
|
|
74
|
+
* directories that no longer exist) untouched. Packages without the wildcard are not managed.
|
|
75
|
+
*/
|
|
76
|
+
function ensureLibraryExports(dir) {
|
|
77
|
+
const pj = _path.default.join(dir, 'package.json');
|
|
78
|
+
const p = JSON.parse(_fs.default.readFileSync(pj, 'utf8'));
|
|
79
|
+
const ex = p.exports;
|
|
80
|
+
if (!ex || typeof ex !== 'object' || !ex['./*']) return false;
|
|
81
|
+
const indexDirs = new Set();
|
|
82
|
+
const walk = (d, rel) => {
|
|
83
|
+
for (const e of _fs.default.readdirSync(d, {
|
|
84
|
+
withFileTypes: true
|
|
85
|
+
})) {
|
|
86
|
+
if (e.name === 'node_modules' || e.name === 'dist' || e.name.startsWith('.')) continue;
|
|
87
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
88
|
+
if (e.isDirectory()) walk(_path.default.join(d, e.name), r);else if (e.name === 'index.ts' && rel) indexDirs.add(rel);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
walk(dir, '');
|
|
92
|
+
// Add-only: hand-written entries (aliases such as js-api's "./u2core") are never touched.
|
|
93
|
+
const next = {
|
|
94
|
+
...ex
|
|
95
|
+
};
|
|
96
|
+
let changed = false;
|
|
97
|
+
for (const rel of [...indexDirs].sort()) {
|
|
98
|
+
const key = `./${rel}`;
|
|
99
|
+
if (!(key in next)) {
|
|
100
|
+
next[key] = {
|
|
101
|
+
types: `./dist/${rel}/index.d.ts`,
|
|
102
|
+
default: `./dist/${rel}/index.js`
|
|
103
|
+
};
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!changed) return false;
|
|
108
|
+
// wildcards last: exports are matched by specificity, but keeping them last reads better
|
|
109
|
+
const wild = ['./*.js', './*'].filter(k => k in next);
|
|
110
|
+
const ordered = {};
|
|
111
|
+
for (const [k, v] of Object.entries(next)) if (!wild.includes(k)) ordered[k] = v;
|
|
112
|
+
for (const k of wild) ordered[k] = next[k];
|
|
113
|
+
p.exports = ordered;
|
|
114
|
+
_fs.default.writeFileSync(pj, JSON.stringify(p, null, 2) + '\n');
|
|
115
|
+
console.log(`package.json exports updated for ${_path.default.basename(dir)}`);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
function isCompiledSibling(dir, rel) {
|
|
119
|
+
const m = rel.match(/^(.*)\.(js|mjs|cjs)$/);
|
|
120
|
+
return !!m && ['.ts', '.tsx'].some(e => _fs.default.existsSync(_path.default.join(dir, m[1] + e)));
|
|
121
|
+
}
|