datagrok-tools 6.7.2 → 6.7.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > datagrok-tools@6.7.2 build /home/runner/work/public/public/tools
2
+ > datagrok-tools@6.7.4 build /home/runner/work/public/public/tools
3
3
  > node build.js
4
4
 
5
5
  Successfully compiled 51 files with Babel.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
- ## 6.7.2 (WIP)
3
+ ## 6.7.4 (2026-09-17)
4
+
5
+ * `grok create`, `grok publish`, `grok test` and `grok run` work inside the pnpm workspace
6
+
7
+ ## 6.7.3 (2026-09-17)
8
+
9
+ * The function-metadata plugin emits decorator roles in camel case again (`fileHandler`, `app`, `viewer`, ...); since 6.7.1 it capitalised them, which broke JS code comparing `options.role` with `DG.FUNC_TYPES`.
10
+ * `grok check` accepts `## v.next` as the top changelog heading, the section the repository keeps unreleased changes in.
11
+
12
+ ## 6.7.2 (2026-09-16)
4
13
 
5
14
  * `grok publish` marks a bundled package for servers older than 1.28.0, which detect one only by a `webpack.config.js` in the archive; the marker is generated at publish time and never written to the package folder.
6
15
 
@@ -11,6 +20,7 @@
11
20
  * `grok config add` — `--key` is now optional: a server reached with a keypair has no developer key to record.
12
21
  * Against a server older than 1.28 — which has no keypair endpoints and refuses their paths before routing — the CLI names the version needed instead of reporting a bare 401, and falls back to the developer key when one is still configured for that server.
13
22
  * `grok login` reports its progress step by step (spinner on a terminal, one line per step in a log), including while it waits for the browser approval.
23
+ * `grok check` — warns when a package pins its own version of a platform-served library (`build-config/platform-deps.json`) instead of `catalog:`; the `common/*.js` shared-library map is read from the same manifest.
14
24
  * `grok s push/migrate` — a migrated entity is stamped in its `metaParams` with `migrated_from` (the source stand URL) and `migrated_on`, so on the target it is clear the entity came from another stand. Written wherever the entity already carries `metaParams` (as `sync_id` is) and stripped before the idempotency comparison, so a re-push of an unchanged entity still reads as identical.
15
25
 
16
26
  ## 6.6.0 (2026-09-13)
@@ -21,7 +21,7 @@ var utils = _interopRequireWildcard(require("../utils/utils"));
21
21
  var color = _interopRequireWildcard(require("../utils/color-utils"));
22
22
  var _const = require("../utils/const");
23
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); }
24
- const warns = ['Latest package version', 'Datagrok API version should contain'];
24
+ const warns = ['Latest package version', 'Datagrok API version should contain', 'is served by the platform'];
25
25
  const forbiddenNames = ['function', 'class', 'export'];
26
26
  /** Static imports of these libraries significantly increase the main bundle size. */
27
27
  const HEAVY_IMPORT_RULES = [
@@ -375,26 +375,29 @@ function checkFuncSignatures(packagePath, files) {
375
375
 
376
376
  return [warnings, errors];
377
377
  }
378
- const sharedLibExternals = {
379
- 'common/html2canvas.min.js': {
380
- 'exceljs': 'ExcelJS'
381
- },
382
- 'common/exceljs.min.js': {
383
- 'html2canvas': 'html2canvas'
384
- },
385
- 'common/ngl_viewer/ngl.js': {
386
- 'NGL': 'NGL'
387
- },
388
- 'common/openchemlib-full.js': {
389
- 'openchemlib/full': 'OCL'
390
- },
391
- 'common/codemirror/codemirror.js': {
392
- 'codemirror': 'CodeMirror'
393
- },
394
- 'common/vue.js': {
395
- 'vue': 'Vue'
378
+
379
+ /** Libraries the platform serves at runtime; versions live in build-config/platform-deps.json. */
380
+ function platformDeps(packagePath) {
381
+ try {
382
+ return require(require.resolve('@datagrok/build-config/platform-deps.json', {
383
+ paths: [packagePath]
384
+ }));
385
+ } catch {
386
+ return {};
396
387
  }
397
- };
388
+ }
389
+
390
+ /** For each `common/*.js` source: the import specifiers a bundled package must externalize, with their globals. */
391
+ function sharedLibExternals(packagePath) {
392
+ const result = {};
393
+ for (const [name, d] of Object.entries(platformDeps(packagePath))) {
394
+ if (d.source) result[d.source] = {
395
+ [name]: d.global,
396
+ ...d.imports
397
+ };
398
+ }
399
+ return result;
400
+ }
398
401
  function checkPackageFile(packagePath, json, options) {
399
402
  const warnings = [];
400
403
  const isPublicPackage = _path.default.basename(_path.default.dirname(packagePath)) === 'packages' && _path.default.basename(_path.default.dirname(_path.default.dirname(packagePath))) === 'public';
@@ -416,6 +419,14 @@ function checkPackageFile(packagePath, json, options) {
416
419
  // if (dt && dt !== 'latest')
417
420
  // warnings.push('File "package.json": "datagrok-tools" dependency must be "latest" version.');
418
421
 
422
+ const platform = platformDeps(packagePath);
423
+ for (const [name, spec] of Object.entries({
424
+ ...json.dependencies,
425
+ ...json.devDependencies
426
+ })) {
427
+ if (name in platform && !spec.startsWith('catalog:')) warnings.push(`File "package.json": "${name}" is served by the platform (${platform[name].version}, ` + `see build-config/platform-deps.json); "${spec}" overrides it. Use "catalog:" unless the override is intended.`);
428
+ }
429
+ const shared = sharedLibExternals(packagePath);
419
430
  if (Array.isArray(json.sources) && json.sources.length > 0) {
420
431
  for (const source of json.sources) {
421
432
  if (typeof source !== 'string') warnings.push(`File "package.json": Only file paths and URLs are allowed in sources. Modify the source ${source}`);
@@ -423,14 +434,15 @@ function checkPackageFile(packagePath, json, options) {
423
434
  if (source.startsWith('common/')) {
424
435
  if (options?.isWebpack && source.endsWith('.js')) {
425
436
  if (options?.externals) {
426
- if (source in sharedLibExternals) {
427
- const [lib, name] = Object.entries(sharedLibExternals[source])[0];
428
- if (!(lib in options.externals && options.externals[lib] === name)) {
437
+ if (source in shared) {
438
+ const expected = Object.entries(shared[source]);
439
+ if (!expected.some(([lib, name]) => options.externals[lib] === name)) {
440
+ const [lib, name] = expected[0];
429
441
  warnings.push(`Webpack config parsing: Consider adding source "${source}" to webpack externals:\n` + `'${lib}': '${name}'\n`);
430
442
  }
431
443
  } else warnings.push(`File "package.json": source "${source}" not in the list of shared libraries`);
432
444
  } else {
433
- warnings.push('Webpack config parsing: External modules not found.\n' + `Consider adding source "${source}" to webpack externals` + (source in sharedLibExternals ? ':\n' + `'${Object.keys(sharedLibExternals[source])[0]}': '${Object.values(sharedLibExternals[source])[0]}'\n` : ''));
445
+ warnings.push('Webpack config parsing: External modules not found.\n' + `Consider adding source "${source}" to webpack externals` + (source in shared ? ':\n' + `'${Object.keys(shared[source])[0]}': '${Object.values(shared[source])[0]}'\n` : ''));
434
446
  }
435
447
  }
436
448
  continue;
@@ -475,9 +487,9 @@ function checkChangelog(packagePath, json) {
475
487
  let regex = /^##[^#].*$/gm;
476
488
  const h2 = clf.match(regex);
477
489
  if (!h2) return ['No versions found in CHANGELOG.md\n'];
478
- regex = /^## \d+\.\d+\.\d+ \((\d{4}-\d{2}-\d{2}|WIP)\)$/;
490
+ regex = /^## (v\.next|\d+\.\d+\.\d+ \((\d{4}-\d{2}-\d{2}|WIP)\))$/;
479
491
  for (const h of h2) {
480
- if (!regex.test(h)) warnings.push(`CHANGELOG: '${h}' does not match the h2 format, expected: ## <version> (<yyyy-mm-dd> | WIP)\n`);
492
+ if (!regex.test(h)) warnings.push(`CHANGELOG: '${h}' does not match the h2 format, expected: ## <version> (<yyyy-mm-dd> | WIP) or ## v.next\n`);
481
493
  }
482
494
  regex = /^## (\d+\.\d+\.\d+)/;
483
495
  const v1 = h2[0].match(regex)?.[1];
@@ -21,12 +21,6 @@ const curFolder = _path.default.basename(curDir);
21
21
  const grokDir = _path.default.join(_os.default.homedir(), '.grok');
22
22
  const confPath = _path.default.join(grokDir, 'config.yaml');
23
23
  const templateDir = _path.default.join(_path.default.dirname(_path.default.dirname(__dirname)), 'package-template');
24
- function isPnpmWorkspace(dir) {
25
- for (let d = _path.default.resolve(dir);; d = _path.default.dirname(d)) {
26
- if (_fs.default.existsSync(_path.default.join(d, 'pnpm-workspace.yaml'))) return true;
27
- if (_path.default.dirname(d) === d) return false;
28
- }
29
- }
30
24
  const confTemplateDir = _path.default.join(_path.default.dirname(_path.default.dirname(__dirname)), 'config-template.yaml');
31
25
  const confTemplate = _jsYaml.default.load(_fs.default.readFileSync(confTemplateDir, {
32
26
  encoding: 'utf-8'
@@ -62,7 +56,7 @@ function createDirectoryContents(name, friendlyName, config, templateDir, packag
62
56
  _package.devDependencies = _package.devDependencies ?? {};
63
57
  // Outside the public/ pnpm workspace the template's workspace:/catalog: specifiers mean
64
58
  // nothing to npm: pin published ranges and bring the toolchain in as a devDependency.
65
- if (!isPnpmWorkspace(packageDir)) {
59
+ if (!utils.isPnpmWorkspace(packageDir)) {
66
60
  const published = {
67
61
  'datagrok-api': `^${require('datagrok-api/package.json').version}`,
68
62
  '@datagrok-libraries/test': '^1.4.0',
@@ -216,8 +210,9 @@ function create(args) {
216
210
  color.success('Successfully created package ' + name);
217
211
  console.log(_entHelpers.help.package(ts));
218
212
  console.log(`\nThe package has the following dependencies:\n${dependencies.join(' ')}\n`);
219
- console.log('Running `npm install` to get the required dependencies...\n');
220
- (0, _child_process.exec)('npm install', {
213
+ const install = utils.isPnpmWorkspace(packageDir) ? 'pnpm install' : 'npm install';
214
+ console.log(`Running \`${install}\` to get the required dependencies...\n`);
215
+ (0, _child_process.exec)(install, {
221
216
  cwd: packageDir
222
217
  }, (err, stdout, stderr) => {
223
218
  if (err) throw err;else console.log(stderr, stdout);
@@ -210,7 +210,7 @@ Options:
210
210
 
211
211
  --all Publish all available packages (run in packages directory)
212
212
  --refresh Publish all available already loaded packages (run in packages directory)
213
- --link Link the package to local packages
213
+ --link Link the package to local packages (no effect in a pnpm workspace checkout)
214
214
  --build Builds the package (the default; kept for compatibility)
215
215
  --skip-build Upload the existing dist/ without rebuilding
216
216
  --release Publish package as release version
@@ -255,7 +255,7 @@ Options:
255
255
  --skip-playwright Skip the Playwright pass; only run Puppeteer/DG.Test
256
256
  --skip-node Skip the Node (browserless) pass; run all tests in the browser
257
257
  --node-only Run only tests annotated {node: true} headless under Node, no browser
258
- --link Link the package to local utils
258
+ --link Link the package to local utils (no effect in a pnpm workspace checkout)
259
259
  --record Records the test execution process in mp4 format
260
260
  --platform Runs only platform tests (applicable for ApiTests package only)
261
261
  --core Runs package & auto tests & core tests (core tests run only from DevTools package)
@@ -30,18 +30,12 @@ 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
- }
39
33
  let verbose = false;
40
34
  let pathMode = false;
41
35
  let devMode = false;
42
36
  let unlink = false;
43
37
  async function link(args) {
44
- if (isPnpmWorkspace(curDir)) {
38
+ if ((0, _utils.isPnpmWorkspace)(curDir)) {
45
39
  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
40
  return true;
47
41
  }
@@ -49,7 +43,7 @@ async function link(args) {
49
43
  devMode = args.dev ?? false;
50
44
  pathMode = args.path ?? false;
51
45
  unlink = args.unlink ?? false;
52
- let collectedPackages = collectAvaliablePackages(args['repo-only']);
46
+ const collectedPackages = collectAvaliablePackages(args['repo-only']);
53
47
  packagesInRepo = collectedPackages.packagesInRepo;
54
48
  packagesOutOfRepo = collectedPackages.packagesOutOfRepo;
55
49
  localPackageDependencies = [];
@@ -83,16 +77,13 @@ function collectPackagesData(packagePath = curDir) {
83
77
  }
84
78
  function collectPacakgeDataFromJsonObject(object) {
85
79
  let result = [];
86
- for (let dependencyName of Object.keys(object ?? {})) {
80
+ for (const dependencyName of Object.keys(object ?? {})) {
87
81
  if (packagesInRepo[dependencyName]) result = result.concat(parsePackageDependencies(dependencyName, _path.default.dirname(packagesInRepo[dependencyName].path)));else if (packagesOutOfRepo[dependencyName]) result = result.concat(parsePackageDependencies(dependencyName, _path.default.dirname(packagesOutOfRepo[dependencyName].path)));
88
82
  }
89
83
  return result;
90
84
  }
91
- function toCamelCase(input) {
92
- return input.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
93
- }
94
85
  function parsePackageDependencies(dependencyName, pathToLink) {
95
- let result = [];
86
+ const result = [];
96
87
  if (!packagesToLink.has(dependencyName)) {
97
88
  packagesToLink.add(dependencyName);
98
89
  localPackageDependencies.push(new PackageData(pathToLink));
@@ -101,8 +92,8 @@ function parsePackageDependencies(dependencyName, pathToLink) {
101
92
  return result;
102
93
  }
103
94
  function collectAvaliablePackages(noOutLink = false) {
104
- let repositoryPackages = collectAvaliablePackagesPathesFromDir(repositoryDir);
105
- let commonPakcages = collectAvaliablePackagesPathesFromDir(containerDir, repositoryDir);
95
+ const repositoryPackages = collectAvaliablePackagesPathesFromDir(repositoryDir);
96
+ const commonPakcages = collectAvaliablePackagesPathesFromDir(containerDir, repositoryDir);
106
97
  const packagesInRepoBaseInfo = parsePackages(repositoryPackages);
107
98
  const packagesOutOfRepoBaseInfo = noOutLink ? [] : parsePackages(commonPakcages);
108
99
  const packagesInRepo = Object.fromEntries(packagesInRepoBaseInfo.map(({
@@ -127,8 +118,8 @@ function collectAvaliablePackagesPathesFromDir(dir, dirToExclude = '') {
127
118
  return res;
128
119
  }
129
120
  function parsePackages(packagPathes) {
130
- let res = packagPathes.map(e => {
131
- let packageData = {};
121
+ const res = packagPathes.map(e => {
122
+ const packageData = {};
132
123
  if (_fs.default.existsSync(e)) {
133
124
  packageData.path = e;
134
125
  const packageJson = JSON.parse(_fs.default.readFileSync(e, 'utf8'));
@@ -142,7 +133,7 @@ function parsePackages(packagPathes) {
142
133
  async function unlinkPackages() {
143
134
  const packages = [...localPackageDependencies];
144
135
  if (currentPackage) packages.push(currentPackage);
145
- for (let packageData of packages) {
136
+ for (const packageData of packages) {
146
137
  if (excludedPackages.includes(packageData.name)) continue;
147
138
  if (verbose) console.log(`Package ${packageData.name} is unlinking`);
148
139
  const packageJsonPath = _path.default.join(packageData.packagePath, 'package.json');
@@ -154,29 +145,27 @@ async function unlinkPackages() {
154
145
  }
155
146
  }
156
147
  function updateDependenciesToVersion(packageData, dependecyNode) {
157
- for (let dependency of packageData.dependencies) {
148
+ for (const dependency of packageData.dependencies) {
158
149
  if (excludedPackages.includes(dependency)) continue;
159
150
  if (dependecyNode[dependency] && !versionDependencyRegex.test(dependecyNode[dependency])) {
160
151
  const packageToLink = (localPackageDependencies.filter(e => e.name === dependency) ?? [])[0];
161
- if (packageToLink) {
162
- dependecyNode[dependency] = `^${packagesInRepo[dependency]?.version ?? packagesOutOfRepo[dependency]?.version}`;
163
- }
152
+ if (packageToLink) dependecyNode[dependency] = `^${packagesInRepo[dependency]?.version ?? packagesOutOfRepo[dependency]?.version}`;
164
153
  }
165
154
  }
166
155
  return dependecyNode;
167
156
  }
168
157
  async function linkPackages() {
169
158
  let anyChanges = true;
170
- for (let element of excludedPackages) packagesToLink.delete(element);
159
+ for (const element of excludedPackages) packagesToLink.delete(element);
171
160
  if (verbose) {
172
161
  console.log('Packages to link:');
173
162
  console.log(localPackageDependencies);
174
163
  }
175
164
  while (anyChanges && packagesToLink.size > 0) {
176
165
  anyChanges = false;
177
- let mapElements = localPackageDependencies.filter(x => x.dependencies.every(i => !packagesToLink.has(i)) && packagesToLink.has(x.name) && !excludedPackages.includes(x.name));
166
+ const mapElements = localPackageDependencies.filter(x => x.dependencies.every(i => !packagesToLink.has(i)) && packagesToLink.has(x.name) && !excludedPackages.includes(x.name));
178
167
  if (mapElements.length === 0) break;
179
- for (let element of mapElements) {
168
+ for (const element of mapElements) {
180
169
  if (verbose) console.log(`Package ${element.name} is linking`);
181
170
  if (pathMode) await linkPathMode(element);
182
171
  await (0, _utils.runScript)(`npm install`, element.packagePath);
@@ -187,7 +176,7 @@ async function linkPackages() {
187
176
  }
188
177
  if (anyChanges === false) throw new Error(`There is loop with next packages: ${JSON.stringify(Array.from(packagesToLink)).toString()}`);
189
178
  }
190
- let names = localPackageDependencies.map(x => x.name);
179
+ const names = localPackageDependencies.map(x => x.name);
191
180
  if (currentPackage && pathMode) await linkPathMode(currentPackage);
192
181
  await (0, _utils.runScript)(`npm install`, curDir);
193
182
  if (pathMode === false) await (0, _utils.runScript)(`npm link ${names.join(' ')}`, curDir);
@@ -208,7 +197,7 @@ async function linkPathMode(packageData) {
208
197
  function updateDependenciesToLocal(packageData, dependecyNode) {
209
198
  const backRouteForCont = `../`.repeat(packageData.packagePath.replace(containerDir, '').split(_path.default.sep).length - 1);
210
199
  const backRouteForRepo = `../`.repeat(packageData.packagePath.replace(repositoryDir, '').split(_path.default.sep).length - 1);
211
- for (let dependency of packageData.dependencies) {
200
+ for (const dependency of packageData.dependencies) {
212
201
  if (excludedPackages.includes(dependency)) continue;
213
202
  if (dependecyNode[dependency]) {
214
203
  const packageToLink = (localPackageDependencies.filter(e => e.name === dependency) ?? [])[0];
@@ -222,7 +211,7 @@ function updateDependenciesToLocal(packageData, dependecyNode) {
222
211
  class PackageData {
223
212
  dependencies = [];
224
213
  constructor(packagePath) {
225
- let packageJsonData = collectPackagesData(packagePath);
214
+ const packageJsonData = collectPackagesData(packagePath);
226
215
  this.name = packageJsonData.name;
227
216
  this.packagePath = packagePath;
228
217
  this.dependencies = packageJsonData.dependencies;
@@ -374,7 +374,9 @@ async function processDockerImages(packageName, version, registry, devKey, host,
374
374
  result = pushImage(img.imageName, registryTag, registry);
375
375
  if (!result) {
376
376
  const fallback = await fallbackImage(img, host, devKey, registry, version, contentHash);
377
- if (fallback.serverError) color.error(`Cannot resolve fallback: ${fallback.serverError}`);else if (fallback.image) color.warn(`Falling back to ${fallback.image}` + `${fallback.hashMatch === false ? ' (hash mismatch — container will run older code)' : ''}`);else color.error(`No published image available for ${img.imageName}. No container will be available.`);
377
+ if (fallback.serverError) color.error(`Cannot resolve fallback: ${fallback.serverError}`);else if (fallback.image) {
378
+ color.warn(`Falling back to ${fallback.image}` + `${fallback.hashMatch === false ? ' (hash mismatch — container will run older code)' : ''}`);
379
+ } else color.error(`No published image available for ${img.imageName}. No container will be available.`);
378
380
  result = fallback;
379
381
  }
380
382
  } else {
@@ -700,28 +702,24 @@ async function publish(args) {
700
702
  }
701
703
  color.log('Loading packages:');
702
704
  await (0, _testUtils.loadPackages)(curDir, packagesToLoad.join(' '), host, false, false, args.link, args.release);
703
- } else {
704
- if (args.link) {
705
- color.log('Linking');
706
- await utils.runScript(`npm install`, curDir);
707
- await utils.runScript(`grok link`, curDir);
708
- await utils.runScript(`npm run build`, curDir);
709
- }
710
- return await publishPackage(args);
711
- }
705
+ } else return await publishPackage(args);
712
706
  return true;
713
707
  }
708
+
709
+ // --link means nothing inside the pnpm workspace: the root install already linked every in-repo dependency
710
+ async function buildPackage(args) {
711
+ const workspace = utils.isPnpmWorkspace(curDir);
712
+ const link = args.link && !workspace;
713
+ if (!link && (args['skip-build'] || args.rebuild || !_fs.default.existsSync(_path.default.join(curDir, 'src')))) return;
714
+ color.log(link ? 'Linking and building' : 'Building');
715
+ if (link || !workspace && !_fs.default.existsSync(_path.default.join(curDir, 'node_modules'))) await utils.runScript('npm install', curDir);
716
+ if (link) await utils.runScript(`${utils.grokCommand} link`, curDir);
717
+ await utils.runScript(workspace ? 'pnpm run build' : 'npm run build', curDir);
718
+ }
714
719
  async function publishPackage(args) {
715
720
  const nArgs = args['_'].length;
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);
724
- }
721
+ await buildPackage(args);
722
+ if (args['skip-build'] && !args.rebuild && _fs.default.existsSync(_path.default.join(curDir, 'src')) && !_fs.default.existsSync(_path.default.join(curDir, 'dist', 'package.js'))) return color.error('dist/package.js not found: build the package first, or run without --skip-build');
725
723
  if (args.debug && args.release) {
726
724
  color.error('Incompatible options: --debug and --release');
727
725
  return false;
@@ -755,7 +753,9 @@ async function publishPackage(args) {
755
753
 
756
754
  // Update the developer key
757
755
  if (args.key) key = args.key;
758
- if (!key && !keypair.keypairFor(url)) return color.warn(`No credentials for ${url}. Run \`grok login ${host}\`, ` + 'or pass a developer key with `--key` (deprecated).');
756
+ if (!key && !keypair.keypairFor(url)) {
757
+ return color.warn(`No credentials for ${url}. Run \`grok login ${host}\`, ` + 'or pass a developer key with `--key` (deprecated).');
758
+ }
759
759
 
760
760
  // Get the package name
761
761
  if (!_fs.default.existsSync(packDir)) return color.error('`package.json` doesn\'t exist');
@@ -39,7 +39,8 @@ function openBrowser(url) {
39
39
  }
40
40
  const MISSING_MODULE_PATTERNS = ['cannot find module', 'module not found', 'can\'t resolve'];
41
41
  async function buildPackage(dir) {
42
- const buildCmd = 'npm run build -- --env incremental';
42
+ const workspace = utils.isPnpmWorkspace(dir);
43
+ const buildCmd = workspace ? 'pnpm run build' : 'npm run build -- --env incremental';
43
44
  const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(dir, 'package.json'), 'utf-8'));
44
45
  const name = packageJson.friendlyName || packageJson.name;
45
46
  console.log(`Building ${name}...`);
@@ -50,9 +51,10 @@ async function buildPackage(dir) {
50
51
  } catch (error) {
51
52
  const msg = (error?.message ?? '').toLowerCase();
52
53
  if (MISSING_MODULE_PATTERNS.some(p => msg.includes(p))) {
53
- color.warn('Missing modules detected, running npm install...');
54
+ const install = workspace ? 'pnpm install --frozen-lockfile' : 'npm install';
55
+ color.warn(`Missing modules detected, running ${install}...`);
54
56
  try {
55
- await utils.runScript('npm install', dir, color.isVerbose());
57
+ await utils.runScript(install, dir, color.isVerbose());
56
58
  await utils.runScript(buildCmd, dir, color.isVerbose());
57
59
  color.success(`Successfully built ${name}`);
58
60
  return true;
@@ -70,7 +72,7 @@ async function buildPackage(dir) {
70
72
  async function run(args) {
71
73
  color.setVerbose(args.verbose || false);
72
74
 
73
- // Step 1: Build (skip npm install to preserve npm link; retry with npm install only if needed)
75
+ // Step 1: Build (skip the install to preserve npm link; retry with an install only if needed)
74
76
  const built = await buildPackage(process.cwd());
75
77
  if (!built) return false;
76
78
 
@@ -251,10 +251,8 @@ async function loadPackage(packageDir, dirName, hostString, skipPublish, skipBui
251
251
  if (skipPublish != true) {
252
252
  process.stdout.write(`Building and publishing ${dirName}...`);
253
253
  try {
254
- await utils.runScript(`npm install`, packageDir);
255
- if (linkPackage) await utils.runScript(`grok link`, packageDir);
256
- if (skipBuild != true) await utils.runScript(`npm run build`, packageDir);
257
- await utils.runScript(`grok publish ${hostString}${release ? ' --release' : ''}`, packageDir);
254
+ const flags = `${release ? ' --release' : ''}${linkPackage ? ' --link' : ''}${skipBuild ? ' --skip-build' : ''}`;
255
+ await utils.runScript(`${utils.grokCommand} publish ${hostString}${flags}`, packageDir);
258
256
  } catch (e) {
259
257
  process.stdout.write(' FAILED\n');
260
258
  throw e;
@@ -19,7 +19,7 @@ exports.getScriptDescription = getScriptDescription;
19
19
  exports.getScriptInputs = getScriptInputs;
20
20
  exports.getScriptName = getScriptName;
21
21
  exports.getScriptOutputType = getScriptOutputType;
22
- exports.headerTags = void 0;
22
+ exports.headerTags = exports.grokCommand = void 0;
23
23
  exports.isConnectivityError = isConnectivityError;
24
24
  exports.isEmpty = isEmpty;
25
25
  exports.isPackageDir = isPackageDir;
@@ -310,6 +310,7 @@ function isPnpmWorkspace(dir) {
310
310
  if (_path.default.dirname(d) === d) return false;
311
311
  }
312
312
  }
313
+ const grokCommand = exports.grokCommand = `"${process.execPath}" "${_path.default.join(__dirname, '..', 'grok.js')}"`;
313
314
  async function runScript(script, path, verbose = false) {
314
315
  try {
315
316
  const {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/datagrok-ai/public.git"
6
6
  },
7
- "version": "6.7.2",
7
+ "version": "6.7.4",
8
8
  "description": "Utility to upload and publish packages to Datagrok",
9
9
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
10
10
  "dependencies": {
@@ -19,7 +19,7 @@ const {
19
19
  } = require('../bin/utils/func-generation');
20
20
 
21
21
  // Not imported from commands/migrate: that module loads ts-morph (~1.5 s) at require time.
22
- const toCamelCase = (s) => s.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join('');
22
+ const toCamelCase = (s) => s.replace(/[-_ ]+(\w)/g, (_, c) => c.toUpperCase()).replace(/^[A-Z]/, (c) => c.toLowerCase());
23
23
 
24
24
  let _api;
25
25
  const api = (...a) => (_api ??= require('../bin/commands/api').api)(...a);