@jbrowse/cli 4.3.0 → 5.0.0-beta.1
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 +331 -57
- package/bin/run +1 -1
- package/bundle/index.js +457 -2574
- 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 +173 -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 +178 -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-pif/cigar-utils.js +62 -19
- package/dist/commands/make-pif/index.js +98 -11
- package/dist/commands/make-pif/pif-generator.js +276 -42
- 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/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 +5394 -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 +457 -0
- package/dist/index.js +99 -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/file-utils.js +0 -30
- package/dist/commands/track-utils.js +0 -74
- package/dist/util.js +0 -35
|
@@ -1,114 +1,91 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
* Parses a comma-separated string into an array of trimmed, non-empty strings
|
|
6
|
-
*/
|
|
7
|
-
export function parseCommaSeparatedString(value) {
|
|
8
|
-
return (value
|
|
9
|
-
?.split(',')
|
|
10
|
-
.map(s => s.trim())
|
|
11
|
-
.filter(Boolean) ?? []);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Validates and parses a prefix size value
|
|
15
|
-
*/
|
|
16
|
-
export function validatePrefixSize(value) {
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { supported } from '../../types/common.js';
|
|
3
|
+
import { parseCommaSeparatedString, readConfigFile, resolveConfigPath, } from '../../utils.js';
|
|
4
|
+
function validatePrefixSize(value) {
|
|
17
5
|
if (!value) {
|
|
18
6
|
return undefined;
|
|
19
7
|
}
|
|
20
8
|
const parsed = typeof value === 'number' ? value : parseInt(value, 10);
|
|
21
|
-
if (isNaN(parsed) || parsed
|
|
9
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
22
10
|
throw new Error(`Invalid prefixSize: "${value}". Must be a positive number.`);
|
|
23
11
|
}
|
|
24
12
|
return parsed;
|
|
25
13
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Prepares common indexDriver parameters from raw flag values
|
|
28
|
-
*/
|
|
29
14
|
export function prepareIndexDriverFlags(flags) {
|
|
30
15
|
return {
|
|
31
16
|
attributes: parseCommaSeparatedString(flags.attributes),
|
|
32
17
|
typesToExclude: parseCommaSeparatedString(flags.exclude),
|
|
18
|
+
// undefined rather than [] when the flag is absent, because an empty allow
|
|
19
|
+
// list has to mean "no allow list" and not "index nothing"
|
|
20
|
+
typesToInclude: flags.include
|
|
21
|
+
? parseCommaSeparatedString(flags.include)
|
|
22
|
+
: undefined,
|
|
33
23
|
quiet: flags.quiet ?? false,
|
|
34
24
|
prefixSize: validatePrefixSize(flags.prefixSize),
|
|
35
25
|
};
|
|
36
26
|
}
|
|
37
|
-
export function
|
|
38
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
39
|
-
}
|
|
40
|
-
export function writeConf(obj, configPath) {
|
|
41
|
-
fs.writeFileSync(configPath, JSON.stringify(obj, null, 2));
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Loads config and prepares output location for indexing
|
|
45
|
-
*/
|
|
46
|
-
export async function loadConfigForIndexing(target, out, resolveConfigPath) {
|
|
27
|
+
export async function loadConfigForIndexing(target, out) {
|
|
47
28
|
const configPath = await resolveConfigPath(target, out);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return { config, configPath, outLocation };
|
|
52
|
-
}
|
|
53
|
-
export function ensureTrixDir(outLocation) {
|
|
54
|
-
const trixDir = path.join(outLocation, 'trix');
|
|
55
|
-
if (!fs.existsSync(trixDir)) {
|
|
56
|
-
fs.mkdirSync(trixDir, { recursive: true });
|
|
57
|
-
}
|
|
58
|
-
return trixDir;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Extracts all assembly names from a config object
|
|
62
|
-
* Handles both single assembly (config.assembly) and multiple assemblies (config.assemblies)
|
|
63
|
-
*/
|
|
64
|
-
function extractAssemblyNamesFromConfig(config) {
|
|
65
|
-
if (config.assemblies) {
|
|
66
|
-
return config.assemblies.map(a => a.name);
|
|
67
|
-
}
|
|
68
|
-
if (config.assembly) {
|
|
69
|
-
return [config.assembly.name];
|
|
70
|
-
}
|
|
71
|
-
return [];
|
|
29
|
+
// readConfigFile, not a bare readFileSync: run from the wrong directory, this
|
|
30
|
+
// is the error the user sees, and it should name the config.json it looked for
|
|
31
|
+
const config = await readConfigFile(configPath);
|
|
32
|
+
return { config, configPath, outLocation: path.dirname(configPath) };
|
|
72
33
|
}
|
|
73
34
|
export function getAssemblyNames(config, assemblies) {
|
|
74
35
|
const asms = assemblies
|
|
75
36
|
? parseCommaSeparatedString(assemblies)
|
|
76
|
-
:
|
|
37
|
+
: (config.assemblies?.map(a => a.name) ??
|
|
38
|
+
(config.assembly ? [config.assembly.name] : []));
|
|
77
39
|
if (!asms.length) {
|
|
78
40
|
throw new Error('No assemblies found');
|
|
79
41
|
}
|
|
80
42
|
return asms;
|
|
81
43
|
}
|
|
82
|
-
export function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
44
|
+
export function formatDryRun(trackConfigs) {
|
|
45
|
+
return trackConfigs.map(t => `${t.trackId}\t${t.adapter?.type}`).join('\n');
|
|
46
|
+
}
|
|
47
|
+
// why a track was left out, or undefined if it is indexable
|
|
48
|
+
function skipReason(track, assemblyName, excludeSet) {
|
|
49
|
+
if (assemblyName && !track.assemblyNames.includes(assemblyName)) {
|
|
50
|
+
return `not in assembly '${assemblyName}'`;
|
|
86
51
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
52
|
+
else if (excludeSet.has(track.trackId)) {
|
|
53
|
+
return 'excluded with --excludeTracks';
|
|
54
|
+
}
|
|
55
|
+
else if (track.metadata?.skipTextIndex) {
|
|
56
|
+
return 'metadata.skipTextIndex is set';
|
|
57
|
+
}
|
|
58
|
+
else if (!supported(track.adapter?.type)) {
|
|
59
|
+
return `adapter type ${track.adapter?.type} is not indexable`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function getTrackConfigs(config, trackIds, assemblyName, excludeTrackIds) {
|
|
66
|
+
const tracks = config.tracks ?? [];
|
|
67
|
+
// when specific trackIds are requested every one must exist; otherwise
|
|
68
|
+
// consider all tracks in the config
|
|
69
|
+
const requested = trackIds?.length
|
|
70
|
+
? trackIds.map(trackId => {
|
|
71
|
+
const track = tracks.find(t => t.trackId === trackId);
|
|
72
|
+
if (track) {
|
|
73
|
+
return track;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw new Error(`Track not found in config.json for trackId ${trackId}, please add track configuration before indexing.`);
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
: tracks;
|
|
80
|
+
const excludeSet = new Set(excludeTrackIds);
|
|
81
|
+
return requested.filter(track => {
|
|
82
|
+
const reason = skipReason(track, assemblyName, excludeSet);
|
|
83
|
+
// only narrate skips for tracks the user named: sweeping a multi-assembly
|
|
84
|
+
// config otherwise prints a line per track per assembly
|
|
85
|
+
if (reason && trackIds?.length) {
|
|
86
|
+
console.log(`Skipping ${track.trackId}: ${reason}`);
|
|
110
87
|
}
|
|
111
|
-
return
|
|
88
|
+
return !reason;
|
|
112
89
|
});
|
|
113
90
|
}
|
|
114
91
|
export { sanitizeForFilename as sanitizeNameForPath } from '@jbrowse/text-indexing-core';
|
|
@@ -1,22 +1,36 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
import { indexDriver, prepareFileTrackConfigs } from
|
|
4
|
-
import { validateFileInput } from
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { formatDryRun, prepareIndexDriverFlags, sanitizeNameForPath, } from './config-utils.js';
|
|
3
|
+
import { indexDriver, prepareFileTrackConfigs } from './indexing-utils.js';
|
|
4
|
+
import { validateFileInput } from './validators.js';
|
|
5
5
|
export async function indexFileList(flags) {
|
|
6
|
-
const { out, target, fileId, file, attributes, quiet, exclude, prefixSize } = flags;
|
|
6
|
+
const { out, target, fileId, file, attributes, quiet, exclude, include, prefixSize, dryrun, } = flags;
|
|
7
7
|
validateFileInput(file);
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// --out/--target names either the install directory or its config.json, as
|
|
9
|
+
// every other command accepts; the trix output goes beside the config, and
|
|
10
|
+
// pointing at the config.json used to mkdir config.json/trix and fail ENOTDIR
|
|
11
|
+
const outArg = target || out || '.';
|
|
12
|
+
const outLocation = outArg.endsWith('.json') ? path.dirname(outArg) : outArg;
|
|
10
13
|
const trackConfigs = prepareFileTrackConfigs(file, fileId);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
trackConfigs
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
if (dryrun) {
|
|
15
|
+
console.log(formatDryRun(trackConfigs));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const name = trackConfigs.length > 1
|
|
19
|
+
? 'aggregate'
|
|
20
|
+
: sanitizeNameForPath(path.basename(file[0]));
|
|
21
|
+
await indexDriver({
|
|
22
|
+
trackConfigs,
|
|
23
|
+
outLocation,
|
|
24
|
+
name,
|
|
25
|
+
assemblyNames: [],
|
|
26
|
+
...prepareIndexDriverFlags({
|
|
27
|
+
attributes,
|
|
28
|
+
exclude,
|
|
29
|
+
include,
|
|
30
|
+
quiet,
|
|
31
|
+
prefixSize,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
console.log('Successfully created index for these files. See https://jbrowse.org/storybook/lgv/with-aggregate-text-searching/ for info about usage');
|
|
35
|
+
}
|
|
22
36
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { aggregateIndex } from
|
|
2
|
-
export { perTrackIndex } from
|
|
3
|
-
export { indexFileList } from
|
|
4
|
-
export { run } from
|
|
1
|
+
export { aggregateIndex } from './aggregate.js';
|
|
2
|
+
export { perTrackIndex } from './per-track.js';
|
|
3
|
+
export { indexFileList } from './file-list.js';
|
|
4
|
+
export { run } from './command.js';
|
|
@@ -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
|
}
|