@jbrowse/cli 4.3.0 → 5.0.0-beta.10

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.
Files changed (71) hide show
  1. package/README.md +451 -72
  2. package/dist/base.js +0 -3
  3. package/dist/bin.js +2 -1
  4. package/dist/cliFetch.js +3 -0
  5. package/dist/commands/add-assembly/index.js +46 -19
  6. package/dist/commands/add-assembly/utils.js +204 -290
  7. package/dist/commands/add-connection.js +73 -79
  8. package/dist/commands/add-track-json.js +24 -12
  9. package/dist/commands/add-track-utils/adapter-utils.js +189 -288
  10. package/dist/commands/add-track-utils/file-operations.js +27 -11
  11. package/dist/commands/add-track-utils/multiwig.js +47 -0
  12. package/dist/commands/add-track-utils/track-config.js +66 -25
  13. package/dist/commands/add-track-utils/validators.js +36 -20
  14. package/dist/commands/add-track.js +200 -62
  15. package/dist/commands/admin-server/index.js +39 -50
  16. package/dist/commands/admin-server/utils.js +238 -126
  17. package/dist/commands/create.js +18 -34
  18. package/dist/commands/make-density/density-generator.js +180 -0
  19. package/dist/commands/make-density/index.js +139 -0
  20. package/dist/commands/make-pif/index.js +133 -11
  21. package/dist/commands/make-pif/pif-generator.js +243 -43
  22. package/dist/commands/process-utils.js +13 -2
  23. package/dist/commands/remove-track.js +20 -13
  24. package/dist/commands/set-default-session.js +35 -31
  25. package/dist/commands/shared/config-operations.js +3 -10
  26. package/dist/commands/shared/density.js +12 -0
  27. package/dist/commands/shared/sort-utils.js +67 -10
  28. package/dist/commands/shared/validators.js +38 -3
  29. package/dist/commands/sort-bed.js +2 -33
  30. package/dist/commands/sort-gff.js +2 -33
  31. package/dist/commands/text-index/adapter-utils.js +19 -26
  32. package/dist/commands/text-index/aggregate.js +40 -34
  33. package/dist/commands/text-index/command.js +37 -7
  34. package/dist/commands/text-index/config-utils.js +62 -85
  35. package/dist/commands/text-index/file-list.js +32 -18
  36. package/dist/commands/text-index/index.js +4 -4
  37. package/dist/commands/text-index/indexing-utils.js +44 -96
  38. package/dist/commands/text-index/per-track.js +44 -31
  39. package/dist/commands/text-index/validators.js +1 -1
  40. package/dist/commands/upgrade.js +20 -32
  41. package/dist/commands/validate/configManifest.generated.js +7613 -0
  42. package/dist/commands/validate/configSchema.generated.js +20400 -0
  43. package/dist/commands/validate/displayDefaultKeys.js +28 -0
  44. package/dist/commands/validate/index.js +113 -0
  45. package/dist/commands/validate/liftConfig.js +34 -0
  46. package/dist/commands/validate/markRules/aggregateFieldName.js +9 -0
  47. package/dist/commands/validate/markRules/autoBin.js +27 -0
  48. package/dist/commands/validate/markRules/colorScale.js +244 -0
  49. package/dist/commands/validate/markRules/cssColorsLevel4.js +162 -0
  50. package/dist/commands/validate/markRules/jexlStrings.js +23 -0
  51. package/dist/commands/validate/markRules/markProblems.js +532 -0
  52. package/dist/commands/validate/markRules/markRuleFacts.js +6 -0
  53. package/dist/commands/validate/markRules/markSpecs.js +96 -0
  54. package/dist/commands/validate/markRules/markVocabulary.js +57 -0
  55. package/dist/commands/validate/markRules/shapeNameList.js +7 -0
  56. package/dist/commands/validate/markRules/stepChannels.js +55 -0
  57. package/dist/commands/validate/schemaValidate.js +425 -0
  58. package/dist/commands/validate/suggest.js +35 -0
  59. package/dist/commands/validate/types.js +6 -0
  60. package/dist/commands/validate/validateConfig.js +502 -0
  61. package/dist/index.js +104 -50
  62. package/dist/types/common.js +3 -27
  63. package/dist/utils.js +309 -128
  64. package/dist/version.js +1 -1
  65. package/package.json +8 -14
  66. package/bin/run +0 -4
  67. package/bundle/index.js +0 -3686
  68. package/dist/commands/make-pif/cigar-utils.js +0 -24
  69. package/dist/commands/make-pif/file-utils.js +0 -30
  70. package/dist/commands/track-utils.js +0 -74
  71. package/dist/util.js +0 -35
@@ -1,110 +1,58 @@
1
- import path from 'path';
2
- import { Readable } from 'stream';
3
- import { generateMeta, guessAdapterFromFileName, indexGff3, indexVcf, } from '@jbrowse/text-indexing-core';
1
+ import { guessAdapterFromFileName, writeTrixIndex, } from '@jbrowse/text-indexing-core';
4
2
  import { Presets, SingleBar } from 'cli-progress';
5
- import { ixIxxStream } from 'ixixx';
6
- import { getAdapterLocation, getLoc } from "./adapter-utils.js";
7
- import { supported } from "../../types/common.js";
8
- export async function runIxIxx({ readStream, outLocation, name, prefixSize, quiet, }) {
9
- const progressBar = new SingleBar({ format: '{bar} Sorting and writing index...', etaBuffer: 2000 }, Presets.shades_classic);
10
- if (!quiet) {
11
- progressBar.start(1, 0);
12
- }
13
- const ixPath = path.join(outLocation, 'trix', `${name}.ix`);
14
- const ixxPath = path.join(outLocation, 'trix', `${name}.ixx`);
15
- await ixIxxStream(readStream, ixPath, ixxPath, prefixSize);
16
- if (!quiet) {
17
- progressBar.update(1);
18
- progressBar.stop();
19
- }
3
+ import { supported } from '../../types/common.js';
4
+ function bar(format) {
5
+ return new SingleBar({ format, etaBuffer: 2000 }, Presets.shades_classic);
20
6
  }
21
- export async function* indexFiles({ trackConfigs, attributes, outLocation, quiet, typesToExclude, }) {
22
- for (const config of trackConfigs) {
23
- const { adapter, textSearching, trackId } = config;
24
- const { type } = adapter || {};
25
- const { indexingFeatureTypesToExclude = typesToExclude, indexingAttributes = attributes, } = textSearching || {};
26
- const loc = getAdapterLocation(adapter);
27
- if (!loc) {
28
- continue;
29
- }
30
- const progressBar = new SingleBar({
31
- format: `{bar} ${trackId} {percentage}% | ETA: {eta}s`,
32
- etaBuffer: 2000,
33
- }, Presets.shades_classic);
34
- if (type === 'Gff3TabixAdapter' || type === 'Gff3Adapter') {
35
- yield* indexGff3({
36
- config,
37
- attributesToIndex: indexingAttributes,
38
- inLocation: getLoc(loc),
39
- outDir: outLocation,
40
- featureTypesToExclude: indexingFeatureTypesToExclude,
41
- onStart: totalBytes => {
42
- if (!quiet) {
43
- progressBar.start(totalBytes, 0);
44
- }
45
- },
46
- onUpdate: receivedBytes => {
47
- if (!quiet) {
48
- progressBar.update(receivedBytes);
49
- }
50
- },
51
- });
52
- }
53
- else if (type === 'VcfTabixAdapter' || type === 'VcfAdapter') {
54
- yield* indexVcf({
55
- config,
56
- attributesToIndex: indexingAttributes,
57
- inLocation: getLoc(loc),
58
- outDir: outLocation,
59
- onStart: totalBytes => {
60
- if (!quiet) {
61
- progressBar.start(totalBytes, 0);
62
- }
63
- },
64
- onUpdate: receivedBytes => {
65
- if (!quiet) {
66
- progressBar.update(receivedBytes);
67
- }
68
- },
69
- });
70
- }
71
- if (!quiet) {
72
- progressBar.stop();
73
- }
74
- }
75
- }
76
- export async function indexDriver({ trackConfigs, attributes, outLocation, name, quiet, typesToExclude, assemblyNames, prefixSize, }) {
77
- const readStream = Readable.from(indexFiles({
78
- trackConfigs,
79
- attributes,
80
- outLocation,
81
- quiet,
82
- typesToExclude,
83
- }));
84
- await runIxIxx({
85
- readStream,
86
- outLocation,
87
- name,
88
- prefixSize,
89
- quiet,
90
- });
91
- generateMeta({
92
- configs: trackConfigs,
93
- attributesToIndex: attributes,
7
+ export async function indexDriver({ trackConfigs, policy, outLocation, name, quiet, assemblyNames, prefixSize, }) {
8
+ let sortBar;
9
+ await writeTrixIndex({
10
+ tracks: trackConfigs,
94
11
  outDir: outLocation,
95
12
  name,
96
- featureTypesToExclude: typesToExclude,
13
+ policy,
97
14
  assemblyNames,
15
+ prefixSize,
16
+ onSort: quiet
17
+ ? undefined
18
+ : () => {
19
+ sortBar = bar('{bar} Sorting and writing index...');
20
+ sortBar.start(1, 0);
21
+ },
22
+ makeProgress: quiet
23
+ ? undefined
24
+ : trackId => {
25
+ const progressBar = bar(
26
+ // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {bar}/{percentage}/{eta} are cli-progress format tokens, not JS interpolation
27
+ `{bar} ${trackId} {percentage}% | ETA: {eta}s`);
28
+ return {
29
+ onStart: totalBytes => {
30
+ progressBar.start(totalBytes, 0);
31
+ },
32
+ onUpdate: receivedBytes => {
33
+ progressBar.update(receivedBytes);
34
+ },
35
+ onDone: () => {
36
+ progressBar.stop();
37
+ },
38
+ };
39
+ },
98
40
  });
41
+ sortBar?.update(1);
42
+ sortBar?.stop();
99
43
  }
100
44
  export function prepareFileTrackConfigs(files, fileIds) {
101
- return files
102
- .map((file, i) => {
45
+ return files.map((file, i) => {
103
46
  const config = guessAdapterFromFileName(file);
104
47
  if (fileIds?.[i]) {
105
48
  config.trackId = fileIds[i];
106
49
  }
50
+ // throw rather than silently filtering: a user who named the file with
51
+ // --file should be told it can't be indexed instead of getting a misleading
52
+ // "successfully created index" with no output
53
+ if (!supported(config.adapter?.type)) {
54
+ throw new Error(`Cannot text-index ${file}: adapter type ${config.adapter?.type} is not indexable (only GFF3, GTF and VCF are supported)`);
55
+ }
107
56
  return config;
108
- })
109
- .filter(fileConfig => supported(fileConfig.adapter?.type));
57
+ });
110
58
  }
@@ -1,18 +1,22 @@
1
- import { createTrixAdapter } from "./adapter-utils.js";
2
- import { getTrackConfigs, loadConfigForIndexing, parseCommaSeparatedString, prepareIndexDriverFlags, writeConf, } from "./config-utils.js";
3
- import { indexDriver } from "./indexing-utils.js";
4
- import { validateAssembliesForPerTrack } from "./validators.js";
5
- import { resolveConfigPath } from "../../utils.js";
1
+ import { parseCommaSeparatedString, writeJsonFile } from '../../utils.js';
2
+ import { createTrixAdapter } from './adapter-utils.js';
3
+ import { formatDryRun, getTrackConfigs, loadConfigForIndexing, prepareIndexDriverFlags, } from './config-utils.js';
4
+ import { indexDriver } from './indexing-utils.js';
5
+ import { validateAssembliesForPerTrack, validateTrackConfigs, } from './validators.js';
6
6
  export async function perTrackIndex(flags) {
7
- const { out, target, tracks, excludeTracks, assemblies, attributes, quiet, force, exclude, prefixSize, } = flags;
8
- const { config, configPath, outLocation } = await loadConfigForIndexing(target, out, resolveConfigPath);
9
- const configTracks = config.tracks || [];
7
+ const { out, target, tracks, excludeTracks, assemblies, attributes, quiet, force, exclude, include, prefixSize, dryrun, } = flags;
10
8
  validateAssembliesForPerTrack(assemblies);
9
+ const { config, configPath, outLocation } = await loadConfigForIndexing(target, out);
10
+ const configTracks = config.tracks ?? [];
11
11
  const confs = getTrackConfigs(config, parseCommaSeparatedString(tracks), undefined, parseCommaSeparatedString(excludeTracks));
12
- if (!confs.length) {
13
- throw new Error('Tracks not found in config.json, please add track configurations before indexing.');
12
+ validateTrackConfigs(confs);
13
+ if (dryrun) {
14
+ console.log(formatDryRun(confs));
15
+ return;
14
16
  }
15
- let hasChanges = false;
17
+ // sequential, not Promise.all: each track's indexer owns the progress bar and
18
+ // streams a whole file through ixIxx
19
+ const indexed = [];
16
20
  for (const trackConfig of confs) {
17
21
  const { textSearching, trackId, assemblyNames } = trackConfig;
18
22
  if (textSearching?.textSearchAdapter && !force) {
@@ -25,27 +29,36 @@ export async function perTrackIndex(flags) {
25
29
  outLocation,
26
30
  name: trackId,
27
31
  assemblyNames,
28
- ...prepareIndexDriverFlags({ attributes, exclude, quiet, prefixSize }),
32
+ ...prepareIndexDriverFlags({
33
+ attributes,
34
+ exclude,
35
+ include,
36
+ quiet,
37
+ prefixSize,
38
+ }),
29
39
  });
30
- if (!textSearching?.textSearchAdapter) {
31
- // modifies track with new text search adapter
32
- const index = configTracks.findIndex(track => trackId === track.trackId);
33
- if (index !== -1) {
34
- configTracks[index] = {
35
- ...trackConfig,
36
- textSearching: {
37
- ...textSearching,
38
- textSearchAdapter: createTrixAdapter(trackId, assemblyNames),
39
- },
40
- };
41
- hasChanges = true;
42
- }
43
- else {
44
- console.warn(`Warning: can't find trackId ${trackId}`);
45
- }
46
- }
40
+ indexed.push(trackConfig);
47
41
  }
48
- if (hasChanges) {
49
- writeConf({ ...config, tracks: configTracks }, configPath);
42
+ // a track that already carried an adapter keeps it — re-indexing under --force
43
+ // refreshes the trix files the existing adapter already points at. The rest
44
+ // gain one naming the files just written. getTrackConfigs returns elements of
45
+ // config.tracks, so every indexed track is somewhere in configTracks.
46
+ const newAdapters = new Map(indexed
47
+ .filter(track => !track.textSearching?.textSearchAdapter)
48
+ .map(track => [
49
+ track.trackId,
50
+ createTrixAdapter(track.trackId, track.assemblyNames),
51
+ ]));
52
+ const updatedTracks = configTracks.map(track => {
53
+ const textSearchAdapter = newAdapters.get(track.trackId);
54
+ return textSearchAdapter
55
+ ? {
56
+ ...track,
57
+ textSearching: { ...track.textSearching, textSearchAdapter },
58
+ }
59
+ : track;
60
+ });
61
+ if (updatedTracks.some((track, i) => track !== configTracks[i])) {
62
+ await writeJsonFile(configPath, { ...config, tracks: updatedTracks });
50
63
  }
51
64
  }
@@ -4,7 +4,7 @@ export function validateTrackConfigs(tracks) {
4
4
  }
5
5
  }
6
6
  export function validateFileInput(file) {
7
- if (!file) {
7
+ if (!file?.length) {
8
8
  throw new Error('Cannot index file list without files');
9
9
  }
10
10
  }
@@ -1,9 +1,7 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { parseArgs } from 'util';
4
- import decompress from 'decompress';
5
- import fetch from "../cliFetch.js";
6
- import { fetchGithubVersions, getBranch, getLatest, getTag, printHelp, } from "../utils.js";
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parseArgs } from 'node:util';
4
+ import { downloadRelease, extractZip, printHelp, printVersions, } from '../utils.js';
7
5
  const description = 'Upgrades JBrowse 2 to latest version';
8
6
  const examples = [
9
7
  '# Upgrades current directory to latest jbrowse release',
@@ -65,8 +63,6 @@ export async function run(args) {
65
63
  args,
66
64
  allowPositionals: true,
67
65
  });
68
- const argsPath = positionals[0];
69
- const { clean, listVersions, tag, url, branch, nightly } = runFlags;
70
66
  if (runFlags.help) {
71
67
  printHelp({
72
68
  options,
@@ -76,41 +72,33 @@ export async function run(args) {
76
72
  });
77
73
  return;
78
74
  }
75
+ const { clean, listVersions, tag, url, branch, nightly } = runFlags;
79
76
  if (listVersions) {
80
- const versions = (await fetchGithubVersions()).map(v => v.tag_name);
81
- console.log(`All JBrowse versions:\n${versions.join('\n')}`);
82
- process.exit(0);
83
- }
84
- if (!argsPath) {
85
- throw new Error('No directory supplied');
77
+ await printVersions();
78
+ return;
86
79
  }
80
+ // localPath is optional: `jbrowse upgrade` upgrades the install in the
81
+ // current directory, as the examples and docs advertise
82
+ const argsPath = positionals[0] ?? '.';
87
83
  if (!fs.existsSync(path.join(argsPath, 'manifest.json'))) {
88
84
  throw new Error(`No manifest.json found in this directory, are you sure it is an
89
85
  existing jbrowse 2 installation?`);
90
86
  }
91
- const locationUrl = url ||
92
- (nightly ? await getBranch('main') : '') ||
93
- (branch ? await getBranch(branch) : '') ||
94
- (tag ? await getTag(tag) : await getLatest());
95
- console.log(`Fetching ${locationUrl}...`);
96
- const response = await fetch(locationUrl);
97
- if (!response.ok) {
98
- throw new Error(`HTTP ${response.status} fetching ${locationUrl}: ${response.statusText}`);
99
- }
100
- const type = response.headers.get('content-type');
101
- if (url &&
102
- type !== 'application/zip' &&
103
- type !== 'application/octet-stream') {
104
- throw new Error('The URL provided does not seem to be a JBrowse installation URL');
105
- }
87
+ const { locationUrl, archive } = await downloadRelease({
88
+ url,
89
+ nightly,
90
+ branch,
91
+ tag,
92
+ });
106
93
  if (clean) {
107
94
  fs.rmSync(path.join(argsPath, 'static'), { recursive: true, force: true });
108
- for (const f of fs
95
+ const workerFiles = fs
109
96
  .readdirSync(argsPath)
110
- .filter(f => f.includes('worker.js'))) {
97
+ .filter(f => f.includes('worker.js'));
98
+ for (const f of workerFiles) {
111
99
  fs.unlinkSync(path.join(argsPath, f));
112
100
  }
113
101
  }
114
- await decompress(Buffer.from(await response.arrayBuffer()), argsPath);
102
+ await extractZip(archive, argsPath);
115
103
  console.log(`Unpacked ${locationUrl} at ${argsPath}`);
116
104
  }