@jbrowse/cli 4.3.0 → 5.0.0-beta.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/README.md +413 -57
- package/bin/run +1 -1
- package/bundle/index.js +550 -2659
- package/dist/base.js +0 -3
- package/dist/bin.js +2 -1
- package/dist/cliFetch.js +3 -0
- package/dist/commands/add-assembly/index.js +46 -19
- package/dist/commands/add-assembly/utils.js +204 -290
- package/dist/commands/add-connection.js +73 -79
- package/dist/commands/add-track-json.js +24 -12
- package/dist/commands/add-track-utils/adapter-utils.js +222 -288
- package/dist/commands/add-track-utils/file-operations.js +27 -11
- package/dist/commands/add-track-utils/multiwig.js +47 -0
- package/dist/commands/add-track-utils/track-config.js +68 -25
- package/dist/commands/add-track-utils/validators.js +36 -20
- package/dist/commands/add-track.js +201 -62
- package/dist/commands/admin-server/index.js +30 -49
- package/dist/commands/admin-server/utils.js +235 -125
- package/dist/commands/create.js +18 -34
- package/dist/commands/make-density/density-generator.js +180 -0
- package/dist/commands/make-density/index.js +139 -0
- package/dist/commands/make-pif/index.js +128 -11
- package/dist/commands/make-pif/pif-generator.js +249 -43
- package/dist/commands/process-utils.js +13 -2
- package/dist/commands/remove-track.js +20 -13
- package/dist/commands/set-default-session.js +35 -31
- package/dist/commands/shared/config-operations.js +3 -10
- package/dist/commands/shared/density.js +12 -0
- package/dist/commands/shared/sort-utils.js +67 -10
- package/dist/commands/shared/validators.js +38 -3
- package/dist/commands/sort-bed.js +2 -33
- package/dist/commands/sort-gff.js +2 -33
- package/dist/commands/text-index/adapter-utils.js +6 -23
- package/dist/commands/text-index/aggregate.js +40 -34
- package/dist/commands/text-index/command.js +35 -7
- package/dist/commands/text-index/config-utils.js +60 -83
- package/dist/commands/text-index/file-list.js +32 -18
- package/dist/commands/text-index/index.js +4 -4
- package/dist/commands/text-index/indexing-utils.js +49 -73
- package/dist/commands/text-index/per-track.js +44 -31
- package/dist/commands/text-index/validators.js +1 -1
- package/dist/commands/upgrade.js +20 -32
- package/dist/commands/validate/configManifest.generated.js +5765 -0
- package/dist/commands/validate/displayDefaultKeys.js +28 -0
- package/dist/commands/validate/index.js +97 -0
- package/dist/commands/validate/suggest.js +35 -0
- package/dist/commands/validate/types.js +6 -0
- package/dist/commands/validate/validateConfig.js +522 -0
- package/dist/index.js +104 -50
- package/dist/types/common.js +3 -27
- package/dist/utils.js +309 -128
- package/dist/version.js +1 -1
- package/package.json +8 -10
- package/dist/commands/make-pif/cigar-utils.js +0 -24
- package/dist/commands/make-pif/file-utils.js +0 -30
- package/dist/commands/track-utils.js +0 -74
- package/dist/util.js +0 -35
|
@@ -1,85 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { TRIX_DIR, generateMeta, guessAdapterFromFileName, indexFiles, trixFilePaths, } from '@jbrowse/text-indexing-core';
|
|
4
5
|
import { Presets, SingleBar } from 'cli-progress';
|
|
5
6
|
import { ixIxxStream } from 'ixixx';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { supported } from '../../types/common.js';
|
|
8
|
+
// every index artifact lands in <outLocation>/trix, so the directory is created
|
|
9
|
+
// here rather than by each caller: --dryrun goes nowhere near indexDriver and so
|
|
10
|
+
// no longer leaves an empty trix/ behind
|
|
11
|
+
function ensureTrixDir(outLocation) {
|
|
12
|
+
fs.mkdirSync(path.join(outLocation, TRIX_DIR), { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
async function runIxIxx({ readStream, outLocation, name, prefixSize, quiet, }) {
|
|
9
15
|
const progressBar = new SingleBar({ format: '{bar} Sorting and writing index...', etaBuffer: 2000 }, Presets.shades_classic);
|
|
10
16
|
if (!quiet) {
|
|
11
17
|
progressBar.start(1, 0);
|
|
12
18
|
}
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
await ixIxxStream(readStream, ixPath, ixxPath, prefixSize);
|
|
19
|
+
const { ix, ixx } = trixFilePaths(outLocation, name);
|
|
20
|
+
await ixIxxStream(readStream, ix, ixx, prefixSize);
|
|
16
21
|
if (!quiet) {
|
|
17
22
|
progressBar.update(1);
|
|
18
23
|
progressBar.stop();
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
|
-
export async function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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) {
|
|
26
|
+
export async function indexDriver({ trackConfigs, attributes, outLocation, name, quiet, typesToExclude, typesToInclude, assemblyNames, prefixSize, }) {
|
|
27
|
+
ensureTrixDir(outLocation);
|
|
28
|
+
const readStream = Readable.from(indexFiles({
|
|
29
|
+
tracks: trackConfigs,
|
|
30
|
+
attributesToIndex: attributes,
|
|
31
|
+
outDir: outLocation,
|
|
32
|
+
featureTypesToExclude: typesToExclude,
|
|
33
|
+
featureTypesToInclude: typesToInclude,
|
|
34
|
+
makeProgress: quiet
|
|
35
|
+
? undefined
|
|
36
|
+
: trackId => {
|
|
37
|
+
const progressBar = new SingleBar({
|
|
38
|
+
// eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {bar}/{percentage}/{eta} are cli-progress format tokens, not JS interpolation
|
|
39
|
+
format: `{bar} ${trackId} {percentage}% | ETA: {eta}s`,
|
|
40
|
+
etaBuffer: 2000,
|
|
41
|
+
}, Presets.shades_classic);
|
|
42
|
+
return {
|
|
43
|
+
onStart: totalBytes => {
|
|
61
44
|
progressBar.start(totalBytes, 0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
onUpdate: receivedBytes => {
|
|
65
|
-
if (!quiet) {
|
|
45
|
+
},
|
|
46
|
+
onUpdate: receivedBytes => {
|
|
66
47
|
progressBar.update(receivedBytes);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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,
|
|
48
|
+
},
|
|
49
|
+
onDone: () => {
|
|
50
|
+
progressBar.stop();
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
83
54
|
}));
|
|
84
55
|
await runIxIxx({
|
|
85
56
|
readStream,
|
|
@@ -94,17 +65,22 @@ export async function indexDriver({ trackConfigs, attributes, outLocation, name,
|
|
|
94
65
|
outDir: outLocation,
|
|
95
66
|
name,
|
|
96
67
|
featureTypesToExclude: typesToExclude,
|
|
68
|
+
featureTypesToInclude: typesToInclude,
|
|
97
69
|
assemblyNames,
|
|
98
70
|
});
|
|
99
71
|
}
|
|
100
72
|
export function prepareFileTrackConfigs(files, fileIds) {
|
|
101
|
-
return files
|
|
102
|
-
.map((file, i) => {
|
|
73
|
+
return files.map((file, i) => {
|
|
103
74
|
const config = guessAdapterFromFileName(file);
|
|
104
75
|
if (fileIds?.[i]) {
|
|
105
76
|
config.trackId = fileIds[i];
|
|
106
77
|
}
|
|
78
|
+
// throw rather than silently filtering: a user who named the file with
|
|
79
|
+
// --file should be told it can't be indexed instead of getting a misleading
|
|
80
|
+
// "successfully created index" with no output
|
|
81
|
+
if (!supported(config.adapter?.type)) {
|
|
82
|
+
throw new Error(`Cannot text-index ${file}: adapter type ${config.adapter?.type} is not indexable (only GFF3, GTF and VCF are supported)`);
|
|
83
|
+
}
|
|
107
84
|
return config;
|
|
108
|
-
})
|
|
109
|
-
.filter(fileConfig => supported(fileConfig.adapter?.type));
|
|
85
|
+
});
|
|
110
86
|
}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
-
|
|
13
|
-
|
|
12
|
+
validateTrackConfigs(confs);
|
|
13
|
+
if (dryrun) {
|
|
14
|
+
console.log(formatDryRun(confs));
|
|
15
|
+
return;
|
|
14
16
|
}
|
|
15
|
-
|
|
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({
|
|
32
|
+
...prepareIndexDriverFlags({
|
|
33
|
+
attributes,
|
|
34
|
+
exclude,
|
|
35
|
+
include,
|
|
36
|
+
quiet,
|
|
37
|
+
prefixSize,
|
|
38
|
+
}),
|
|
29
39
|
});
|
|
30
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { parseArgs } from 'util';
|
|
4
|
-
import
|
|
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
|
-
|
|
81
|
-
|
|
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 =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
|
102
|
+
await extractZip(archive, argsPath);
|
|
115
103
|
console.log(`Unpacked ${locationUrl} at ${argsPath}`);
|
|
116
104
|
}
|