@jbrowse/cli 3.5.1 → 3.6.0
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 +291 -607
- package/bin/run +1 -6
- package/bundle/index.js +4459 -0
- package/package.json +12 -37
- package/bin/dev +0 -17
- package/bin/dev.cmd +0 -3
- package/bin/run.cmd +0 -3
- package/lib/base.js +0 -157
- package/lib/commands/add-assembly.js +0 -491
- package/lib/commands/add-connection.js +0 -170
- package/lib/commands/add-track-json.js +0 -67
- package/lib/commands/add-track.js +0 -564
- package/lib/commands/admin-server.js +0 -153
- package/lib/commands/create.js +0 -111
- package/lib/commands/make-pif.js +0 -116
- package/lib/commands/remove-track.js +0 -37
- package/lib/commands/set-default-session.js +0 -98
- package/lib/commands/sort-bed.js +0 -45
- package/lib/commands/sort-gff.js +0 -45
- package/lib/commands/text-index.js +0 -380
- package/lib/commands/upgrade.js +0 -109
- package/lib/fetchWithProxy.js +0 -12
- package/lib/index.js +0 -6
- package/lib/types/common.js +0 -128
- package/lib/types/gff3Adapter.js +0 -73
- package/lib/types/vcfAdapter.js +0 -76
- package/lib/util.js +0 -35
- package/oclif.manifest.json +0 -1169
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const fs_1 = __importDefault(require("fs"));
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const core_1 = require("@oclif/core");
|
|
9
|
-
const base_1 = __importDefault(require("../base"));
|
|
10
|
-
const { rename, copyFile, mkdir, symlink } = fs_1.default.promises;
|
|
11
|
-
function isValidJSON(string) {
|
|
12
|
-
try {
|
|
13
|
-
JSON.parse(string);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch (error) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
class AddAssembly extends base_1.default {
|
|
21
|
-
async getAssembly() {
|
|
22
|
-
let sequence;
|
|
23
|
-
const { args: runArgs, flags: runFlags } = await this.parse(AddAssembly);
|
|
24
|
-
const { sequence: argsSequence } = runArgs;
|
|
25
|
-
if (this.needLoadData(argsSequence) && !runFlags.load) {
|
|
26
|
-
this.error('Please specify the loading operation for this file with --load copy|symlink|move|inPlace', { exit: 110 });
|
|
27
|
-
}
|
|
28
|
-
else if (!this.needLoadData(argsSequence) && runFlags.load) {
|
|
29
|
-
this.error('URL detected with --load flag. Please rerun the function without the --load flag', { exit: 120 });
|
|
30
|
-
}
|
|
31
|
-
let { name } = runFlags;
|
|
32
|
-
let { type } = runFlags;
|
|
33
|
-
if (type) {
|
|
34
|
-
this.debug(`Type is: ${type}`);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
type = this.guessSequenceType(argsSequence);
|
|
38
|
-
this.debug(`No type specified, guessing type: ${type}`);
|
|
39
|
-
}
|
|
40
|
-
if (name) {
|
|
41
|
-
this.debug(`Name is: ${name}`);
|
|
42
|
-
}
|
|
43
|
-
switch (type) {
|
|
44
|
-
case 'indexedFasta': {
|
|
45
|
-
const { skipCheck, force, load, faiLocation } = runFlags;
|
|
46
|
-
let sequenceLocation = await this.resolveFileLocation(argsSequence, !(skipCheck || force), load === 'inPlace');
|
|
47
|
-
this.debug(`FASTA location resolved to: ${sequenceLocation}`);
|
|
48
|
-
let indexLocation = await this.resolveFileLocation(faiLocation || `${argsSequence}.fai`, !(skipCheck || force), load === 'inPlace');
|
|
49
|
-
this.debug(`FASTA index location resolved to: ${indexLocation}`);
|
|
50
|
-
if (!name) {
|
|
51
|
-
name = path_1.default.basename(sequenceLocation, sequenceLocation.endsWith('.fasta') ? '.fasta' : '.fa');
|
|
52
|
-
this.debug(`Guessing name: ${name}`);
|
|
53
|
-
}
|
|
54
|
-
const loaded = load
|
|
55
|
-
? await this.loadData(load, [sequenceLocation, indexLocation])
|
|
56
|
-
: false;
|
|
57
|
-
if (loaded) {
|
|
58
|
-
sequenceLocation = path_1.default.basename(sequenceLocation);
|
|
59
|
-
indexLocation = path_1.default.basename(indexLocation);
|
|
60
|
-
}
|
|
61
|
-
sequence = {
|
|
62
|
-
type: 'ReferenceSequenceTrack',
|
|
63
|
-
trackId: `${name}-ReferenceSequenceTrack`,
|
|
64
|
-
adapter: {
|
|
65
|
-
type: 'IndexedFastaAdapter',
|
|
66
|
-
fastaLocation: {
|
|
67
|
-
uri: sequenceLocation,
|
|
68
|
-
locationType: 'UriLocation',
|
|
69
|
-
},
|
|
70
|
-
faiLocation: { uri: indexLocation, locationType: 'UriLocation' },
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
case 'bgzipFasta': {
|
|
76
|
-
let sequenceLocation = await this.resolveFileLocation(argsSequence, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
77
|
-
this.debug(`compressed FASTA location resolved to: ${sequenceLocation}`);
|
|
78
|
-
let indexLocation = await this.resolveFileLocation(runFlags.faiLocation || `${sequenceLocation}.fai`, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
79
|
-
this.debug(`compressed FASTA index location resolved to: ${indexLocation}`);
|
|
80
|
-
let bgzipIndexLocation = await this.resolveFileLocation(runFlags.gziLocation || `${sequenceLocation}.gzi`, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
81
|
-
this.debug(`bgzip index location resolved to: ${bgzipIndexLocation}`);
|
|
82
|
-
if (!name) {
|
|
83
|
-
name = path_1.default.basename(sequenceLocation, sequenceLocation.endsWith('.fasta.gz') ? '.fasta.gz' : '.fa.gz');
|
|
84
|
-
this.debug(`Guessing name: ${name}`);
|
|
85
|
-
}
|
|
86
|
-
const loaded = runFlags.load
|
|
87
|
-
? await this.loadData(runFlags.load, [
|
|
88
|
-
sequenceLocation,
|
|
89
|
-
indexLocation,
|
|
90
|
-
bgzipIndexLocation,
|
|
91
|
-
])
|
|
92
|
-
: false;
|
|
93
|
-
if (loaded) {
|
|
94
|
-
sequenceLocation = path_1.default.basename(sequenceLocation);
|
|
95
|
-
indexLocation = path_1.default.basename(indexLocation);
|
|
96
|
-
bgzipIndexLocation = path_1.default.basename(bgzipIndexLocation);
|
|
97
|
-
}
|
|
98
|
-
sequence = {
|
|
99
|
-
type: 'ReferenceSequenceTrack',
|
|
100
|
-
trackId: `${name}-ReferenceSequenceTrack`,
|
|
101
|
-
adapter: {
|
|
102
|
-
type: 'BgzipFastaAdapter',
|
|
103
|
-
fastaLocation: {
|
|
104
|
-
uri: sequenceLocation,
|
|
105
|
-
locationType: 'UriLocation',
|
|
106
|
-
},
|
|
107
|
-
faiLocation: { uri: indexLocation, locationType: 'UriLocation' },
|
|
108
|
-
gziLocation: {
|
|
109
|
-
uri: bgzipIndexLocation,
|
|
110
|
-
locationType: 'UriLocation',
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case 'twoBit': {
|
|
117
|
-
let sequenceLocation = await this.resolveFileLocation(argsSequence, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
118
|
-
this.debug(`2bit location resolved to: ${sequenceLocation}`);
|
|
119
|
-
if (!name) {
|
|
120
|
-
name = path_1.default.basename(sequenceLocation, '.2bit');
|
|
121
|
-
this.debug(`Guessing name: ${name}`);
|
|
122
|
-
}
|
|
123
|
-
const loaded = runFlags.load
|
|
124
|
-
? await this.loadData(runFlags.load, [sequenceLocation])
|
|
125
|
-
: false;
|
|
126
|
-
if (loaded) {
|
|
127
|
-
sequenceLocation = path_1.default.basename(sequenceLocation);
|
|
128
|
-
}
|
|
129
|
-
sequence = {
|
|
130
|
-
type: 'ReferenceSequenceTrack',
|
|
131
|
-
trackId: `${name}-ReferenceSequenceTrack`,
|
|
132
|
-
adapter: {
|
|
133
|
-
type: 'TwoBitAdapter',
|
|
134
|
-
twoBitLocation: {
|
|
135
|
-
uri: sequenceLocation,
|
|
136
|
-
locationType: 'UriLocation',
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
case 'chromSizes': {
|
|
143
|
-
let sequenceLocation = await this.resolveFileLocation(argsSequence, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
144
|
-
this.debug(`chrom.sizes location resolved to: ${sequenceLocation}`);
|
|
145
|
-
if (!name) {
|
|
146
|
-
name = path_1.default.basename(sequenceLocation, '.chrom.sizes');
|
|
147
|
-
this.debug(`Guessing name: ${name}`);
|
|
148
|
-
}
|
|
149
|
-
const loaded = runFlags.load
|
|
150
|
-
? await this.loadData(runFlags.load, [sequenceLocation])
|
|
151
|
-
: false;
|
|
152
|
-
if (loaded) {
|
|
153
|
-
sequenceLocation = path_1.default.basename(sequenceLocation);
|
|
154
|
-
}
|
|
155
|
-
sequence = {
|
|
156
|
-
type: 'ReferenceSequenceTrack',
|
|
157
|
-
trackId: `${name}-ReferenceSequenceTrack`,
|
|
158
|
-
adapter: {
|
|
159
|
-
type: 'ChromSizesAdapter',
|
|
160
|
-
chromSizesLocation: {
|
|
161
|
-
uri: sequenceLocation,
|
|
162
|
-
locationType: 'UriLocation',
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
case 'custom': {
|
|
169
|
-
const adapter = await this.readInlineOrFileJson(argsSequence);
|
|
170
|
-
this.debug(`Custom adapter: ${JSON.stringify(adapter)}`);
|
|
171
|
-
if (!name) {
|
|
172
|
-
if (isValidJSON(argsSequence)) {
|
|
173
|
-
this.error('Must provide --name when using custom inline JSON sequence', { exit: 130 });
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
name = path_1.default.basename(argsSequence, '.json');
|
|
177
|
-
}
|
|
178
|
-
this.debug(`Guessing name: ${name}`);
|
|
179
|
-
}
|
|
180
|
-
if (!('type' in adapter)) {
|
|
181
|
-
this.error(`No "type" specified in sequence adapter "${JSON.stringify(adapter)}"`, { exit: 140 });
|
|
182
|
-
}
|
|
183
|
-
sequence = {
|
|
184
|
-
type: 'ReferenceSequenceTrack',
|
|
185
|
-
trackId: `${name}-ReferenceSequenceTrack`,
|
|
186
|
-
adapter,
|
|
187
|
-
};
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return { name, sequence };
|
|
192
|
-
}
|
|
193
|
-
async run() {
|
|
194
|
-
// https://stackoverflow.com/a/35008327/2129219
|
|
195
|
-
const exists = (s) => new Promise(r => {
|
|
196
|
-
fs_1.default.access(s, fs_1.default.constants.F_OK, e => {
|
|
197
|
-
r(!e);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
const { args: runArgs, flags: runFlags } = await this.parse(AddAssembly);
|
|
201
|
-
const output = runFlags.target || runFlags.out || '.';
|
|
202
|
-
if (!(await exists(output))) {
|
|
203
|
-
const dir = output.endsWith('.json') ? path_1.default.dirname(output) : output;
|
|
204
|
-
await mkdir(dir, { recursive: true });
|
|
205
|
-
}
|
|
206
|
-
let isDir = false;
|
|
207
|
-
try {
|
|
208
|
-
isDir = fs_1.default.statSync(output).isDirectory();
|
|
209
|
-
}
|
|
210
|
-
catch (e) { }
|
|
211
|
-
this.target = isDir ? path_1.default.join(output, 'config.json') : output;
|
|
212
|
-
const { sequence: argsSequence } = runArgs;
|
|
213
|
-
this.debug(`Sequence location is: ${argsSequence}`);
|
|
214
|
-
const { name } = runFlags;
|
|
215
|
-
const assembly = await this.getAssembly();
|
|
216
|
-
if (runFlags.alias?.length) {
|
|
217
|
-
this.debug(`Adding assembly aliases: ${runFlags.alias}`);
|
|
218
|
-
assembly.aliases = runFlags.alias;
|
|
219
|
-
}
|
|
220
|
-
if (runFlags.refNameColors) {
|
|
221
|
-
const colors = runFlags.refNameColors
|
|
222
|
-
.split(',')
|
|
223
|
-
.map(color => color.trim());
|
|
224
|
-
this.debug(`Adding refName colors: ${colors}`);
|
|
225
|
-
assembly.refNameColors = colors;
|
|
226
|
-
}
|
|
227
|
-
if (runFlags.refNameAliases) {
|
|
228
|
-
if (runFlags.refNameAliasesType &&
|
|
229
|
-
runFlags.refNameAliasesType === 'custom') {
|
|
230
|
-
const refNameAliasesConfig = await this.readInlineOrFileJson(runFlags.refNameAliases);
|
|
231
|
-
if (!refNameAliasesConfig.type) {
|
|
232
|
-
this.error(`No "type" specified in refNameAliases adapter "${JSON.stringify(refNameAliasesConfig)}"`, { exit: 150 });
|
|
233
|
-
}
|
|
234
|
-
this.debug(`Adding custom refNameAliases config: ${JSON.stringify(refNameAliasesConfig)}`);
|
|
235
|
-
assembly.refNameAliases = {
|
|
236
|
-
adapter: refNameAliasesConfig,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
const refNameAliasesLocation = await this.resolveFileLocation(runFlags.refNameAliases, !(runFlags.skipCheck || runFlags.force), runFlags.load === 'inPlace');
|
|
241
|
-
this.debug(`refName aliases file location resolved to: ${refNameAliasesLocation}`);
|
|
242
|
-
assembly.refNameAliases = {
|
|
243
|
-
adapter: {
|
|
244
|
-
type: 'RefNameAliasAdapter',
|
|
245
|
-
location: {
|
|
246
|
-
uri: refNameAliasesLocation,
|
|
247
|
-
locationType: 'UriLocation',
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
if (runFlags.displayName) {
|
|
254
|
-
assembly.displayName = runFlags.displayName;
|
|
255
|
-
}
|
|
256
|
-
const defaultConfig = {
|
|
257
|
-
assemblies: [],
|
|
258
|
-
configuration: {},
|
|
259
|
-
connections: [],
|
|
260
|
-
defaultSession: {
|
|
261
|
-
name: 'New Session',
|
|
262
|
-
},
|
|
263
|
-
tracks: [],
|
|
264
|
-
};
|
|
265
|
-
let configContents;
|
|
266
|
-
if (fs_1.default.existsSync(this.target)) {
|
|
267
|
-
this.debug(`Found existing config file ${this.target}`);
|
|
268
|
-
configContents = {
|
|
269
|
-
...defaultConfig,
|
|
270
|
-
...(await this.readJsonFile(this.target)),
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
this.debug(`Creating config file ${this.target}`);
|
|
275
|
-
configContents = { ...defaultConfig };
|
|
276
|
-
}
|
|
277
|
-
if (!configContents.assemblies) {
|
|
278
|
-
configContents.assemblies = [];
|
|
279
|
-
}
|
|
280
|
-
const idx = configContents.assemblies.findIndex(configAssembly => configAssembly.name === assembly.name);
|
|
281
|
-
if (idx !== -1) {
|
|
282
|
-
this.debug(`Found existing assembly ${name} in configuration`);
|
|
283
|
-
if (runFlags.overwrite || runFlags.force) {
|
|
284
|
-
this.debug(`Overwriting assembly ${name} in configuration`);
|
|
285
|
-
configContents.assemblies[idx] = assembly;
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
this.error(`Cannot add assembly with name ${assembly.name}, an assembly with that name already exists`, { exit: 160 });
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
configContents.assemblies.push(assembly);
|
|
293
|
-
}
|
|
294
|
-
this.debug(`Writing configuration to file ${this.target}`);
|
|
295
|
-
await this.writeJsonFile(this.target, configContents);
|
|
296
|
-
this.log(`${idx !== -1 ? 'Overwrote' : 'Added'} assembly "${assembly.name}" ${idx !== -1 ? 'in' : 'to'} ${this.target}`);
|
|
297
|
-
}
|
|
298
|
-
guessSequenceType(sequence) {
|
|
299
|
-
if (sequence.endsWith('.fa') ||
|
|
300
|
-
sequence.endsWith('.fna') ||
|
|
301
|
-
sequence.endsWith('.fasta')) {
|
|
302
|
-
return 'indexedFasta';
|
|
303
|
-
}
|
|
304
|
-
if (sequence.endsWith('.fa.gz') ||
|
|
305
|
-
sequence.endsWith('.fna.gz') ||
|
|
306
|
-
sequence.endsWith('.fasta.gz')) {
|
|
307
|
-
return 'bgzipFasta';
|
|
308
|
-
}
|
|
309
|
-
if (sequence.endsWith('.2bit')) {
|
|
310
|
-
return 'twoBit';
|
|
311
|
-
}
|
|
312
|
-
if (sequence.endsWith('.chrom.sizes')) {
|
|
313
|
-
return 'chromSizes';
|
|
314
|
-
}
|
|
315
|
-
if (sequence.endsWith('.json')) {
|
|
316
|
-
return 'custom';
|
|
317
|
-
}
|
|
318
|
-
if (isValidJSON(sequence)) {
|
|
319
|
-
return 'custom';
|
|
320
|
-
}
|
|
321
|
-
return this.error('Could not determine sequence type automatically, add --type to specify it', { exit: 170 });
|
|
322
|
-
}
|
|
323
|
-
needLoadData(location) {
|
|
324
|
-
let locationUrl;
|
|
325
|
-
try {
|
|
326
|
-
locationUrl = new URL(location);
|
|
327
|
-
}
|
|
328
|
-
catch (error) {
|
|
329
|
-
// ignore
|
|
330
|
-
}
|
|
331
|
-
if (locationUrl) {
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
async loadData(load, filePaths) {
|
|
337
|
-
let locationUrl;
|
|
338
|
-
const destination = this.target;
|
|
339
|
-
try {
|
|
340
|
-
locationUrl = new URL(filePaths[0]);
|
|
341
|
-
}
|
|
342
|
-
catch (error) {
|
|
343
|
-
// ignore
|
|
344
|
-
}
|
|
345
|
-
if (locationUrl) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
switch (load) {
|
|
349
|
-
case 'copy': {
|
|
350
|
-
await Promise.all(filePaths.map(async (filePath) => {
|
|
351
|
-
if (!filePath) {
|
|
352
|
-
return undefined;
|
|
353
|
-
}
|
|
354
|
-
return copyFile(filePath, path_1.default.join(path_1.default.dirname(destination), path_1.default.basename(filePath)));
|
|
355
|
-
}));
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
case 'symlink': {
|
|
359
|
-
await Promise.all(filePaths.map(async (filePath) => {
|
|
360
|
-
if (!filePath) {
|
|
361
|
-
return undefined;
|
|
362
|
-
}
|
|
363
|
-
return symlink(path_1.default.resolve(filePath), path_1.default.join(path_1.default.dirname(destination), path_1.default.basename(filePath)));
|
|
364
|
-
}));
|
|
365
|
-
return true;
|
|
366
|
-
}
|
|
367
|
-
case 'move': {
|
|
368
|
-
await Promise.all(filePaths.map(async (filePath) => {
|
|
369
|
-
if (!filePath) {
|
|
370
|
-
return undefined;
|
|
371
|
-
}
|
|
372
|
-
return rename(filePath, path_1.default.join(path_1.default.dirname(destination), path_1.default.basename(filePath)));
|
|
373
|
-
}));
|
|
374
|
-
return true;
|
|
375
|
-
}
|
|
376
|
-
case 'inPlace': {
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
AddAssembly.description = 'Add an assembly to a JBrowse 2 configuration';
|
|
384
|
-
AddAssembly.examples = [
|
|
385
|
-
'# add assembly to installation in current directory. assumes .fai file also exists, and copies GRCh38.fa and GRCh38.fa.fai to current directory',
|
|
386
|
-
'$ jbrowse add-assembly GRCh38.fa --load copy',
|
|
387
|
-
'',
|
|
388
|
-
'# add assembly to a specific jb2 installation path using --out, and copies the .fa and .fa.fai file to /path/to/jb2',
|
|
389
|
-
'$ jbrowse add-assembly GRCh38.fa --out /path/to/jb2/ --load copy',
|
|
390
|
-
'',
|
|
391
|
-
'# force indexedFasta for add-assembly without relying on file extension',
|
|
392
|
-
'$ jbrowse add-assembly GRCh38.xyz --type indexedFasta --load copy',
|
|
393
|
-
'',
|
|
394
|
-
'# add displayName for an assembly',
|
|
395
|
-
'$ jbrowse add-assembly myFile.fa.gz --name hg38 --displayName "Homo sapiens (hg38)"',
|
|
396
|
-
'',
|
|
397
|
-
'# use chrom.sizes file for assembly instead of a fasta file',
|
|
398
|
-
'$ jbrowse add-assembly GRCh38.chrom.sizes --load inPlace',
|
|
399
|
-
'',
|
|
400
|
-
'# add assembly from preconfigured json file, expert option',
|
|
401
|
-
'$ jbrowse add-assembly GRCh38.config.json --load copy',
|
|
402
|
-
'',
|
|
403
|
-
'# add assembly from a 2bit file, also note pointing direct to a URL so no --load flag needed',
|
|
404
|
-
'$ jbrowse add-assembly https://example.com/data/sample.2bit',
|
|
405
|
-
'',
|
|
406
|
-
'# add a bgzip indexed fasta inferred by fa.gz extension. assumes .fa.gz.gzi and .fa.gz.fai files also exists',
|
|
407
|
-
'$ jbrowse add-assembly myfile.fa.gz --load copy',
|
|
408
|
-
];
|
|
409
|
-
AddAssembly.args = {
|
|
410
|
-
sequence: core_1.Args.string({
|
|
411
|
-
required: true,
|
|
412
|
-
description: `sequence file or URL
|
|
413
|
-
|
|
414
|
-
If TYPE is indexedFasta or bgzipFasta, the index file defaults to <location>.fai
|
|
415
|
-
and can be optionally specified with --faiLocation
|
|
416
|
-
If TYPE is bgzipFasta, the gzip index file defaults to <location>.gzi and can be
|
|
417
|
-
optionally specified with --gziLocation`,
|
|
418
|
-
}),
|
|
419
|
-
};
|
|
420
|
-
AddAssembly.flags = {
|
|
421
|
-
type: core_1.Flags.string({
|
|
422
|
-
char: 't',
|
|
423
|
-
description: `type of sequence, by default inferred from sequence file
|
|
424
|
-
|
|
425
|
-
indexedFasta An index FASTA (e.g. .fa or .fasta) file;
|
|
426
|
-
can optionally specify --faiLocation
|
|
427
|
-
|
|
428
|
-
bgzipFasta A block-gzipped and indexed FASTA (e.g. .fa.gz or .fasta.gz) file;
|
|
429
|
-
can optionally specify --faiLocation and/or --gziLocation
|
|
430
|
-
|
|
431
|
-
twoBit A twoBit (e.g. .2bit) file
|
|
432
|
-
|
|
433
|
-
chromSizes A chromosome sizes (e.g. .chrom.sizes) file
|
|
434
|
-
|
|
435
|
-
custom Either a JSON file location or inline JSON that defines a custom
|
|
436
|
-
sequence adapter; must provide --name if using inline JSON`,
|
|
437
|
-
options: ['indexedFasta', 'bgzipFasta', 'twoBit', 'chromSizes', 'custom'],
|
|
438
|
-
}),
|
|
439
|
-
name: core_1.Flags.string({
|
|
440
|
-
char: 'n',
|
|
441
|
-
description: 'Name of the assembly; if not specified, will be guessed using the sequence file name',
|
|
442
|
-
}),
|
|
443
|
-
alias: core_1.Flags.string({
|
|
444
|
-
char: 'a',
|
|
445
|
-
description: 'An alias for the assembly name (e.g. "hg38" if the name of the assembly is "GRCh38");\ncan be specified multiple times',
|
|
446
|
-
multiple: true,
|
|
447
|
-
}),
|
|
448
|
-
displayName: core_1.Flags.string({
|
|
449
|
-
description: 'The display name to specify for the assembly, e.g. "Homo sapiens (hg38)" while the name can be a shorter identifier like "hg38"',
|
|
450
|
-
}),
|
|
451
|
-
faiLocation: core_1.Flags.string({
|
|
452
|
-
description: '[default: <fastaLocation>.fai] FASTA index file or URL',
|
|
453
|
-
}),
|
|
454
|
-
gziLocation: core_1.Flags.string({
|
|
455
|
-
description: '[default: <fastaLocation>.gzi] FASTA gzip index file or URL',
|
|
456
|
-
}),
|
|
457
|
-
refNameAliases: core_1.Flags.string({
|
|
458
|
-
description: 'Reference sequence name aliases file or URL; assumed to be a tab-separated aliases\nfile unless --refNameAliasesType is specified',
|
|
459
|
-
}),
|
|
460
|
-
refNameAliasesType: core_1.Flags.string({
|
|
461
|
-
description: 'Type of aliases defined by --refNameAliases; if "custom", --refNameAliases is either\na JSON file location or inline JSON that defines a custom sequence adapter',
|
|
462
|
-
options: ['aliases', 'custom'],
|
|
463
|
-
dependsOn: ['refNameAliases'],
|
|
464
|
-
}),
|
|
465
|
-
refNameColors: core_1.Flags.string({
|
|
466
|
-
description: 'A comma-separated list of color strings for the reference sequence names; will cycle\nthrough colors if there are fewer colors than sequences',
|
|
467
|
-
}),
|
|
468
|
-
target: core_1.Flags.string({
|
|
469
|
-
description: 'path to config file in JB2 installation directory to write out to.\nCreates ./config.json if nonexistent',
|
|
470
|
-
}),
|
|
471
|
-
out: core_1.Flags.string({
|
|
472
|
-
description: 'synonym for target',
|
|
473
|
-
}),
|
|
474
|
-
help: core_1.Flags.help({ char: 'h' }),
|
|
475
|
-
load: core_1.Flags.string({
|
|
476
|
-
char: 'l',
|
|
477
|
-
description: 'Required flag when using a local file. Choose how to manage the data directory. Copy, symlink, or move the data directory to the JBrowse directory. Or use inPlace to modify the config without doing any file operations',
|
|
478
|
-
options: ['copy', 'symlink', 'move', 'inPlace'],
|
|
479
|
-
}),
|
|
480
|
-
skipCheck: core_1.Flags.boolean({
|
|
481
|
-
description: "Don't check whether or not the sequence file or URL exists or if you are in a JBrowse directory",
|
|
482
|
-
}),
|
|
483
|
-
overwrite: core_1.Flags.boolean({
|
|
484
|
-
description: 'Overwrite existing assembly if one with the same name exists',
|
|
485
|
-
}),
|
|
486
|
-
force: core_1.Flags.boolean({
|
|
487
|
-
char: 'f',
|
|
488
|
-
description: 'Equivalent to `--skipCheck --overwrite`',
|
|
489
|
-
}),
|
|
490
|
-
};
|
|
491
|
-
exports.default = AddAssembly;
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const fs_1 = __importDefault(require("fs"));
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const core_1 = require("@oclif/core");
|
|
9
|
-
const json_parse_better_errors_1 = __importDefault(require("json-parse-better-errors"));
|
|
10
|
-
const base_1 = __importDefault(require("../base"));
|
|
11
|
-
const fetchWithProxy_1 = __importDefault(require("../fetchWithProxy"));
|
|
12
|
-
class AddConnection extends base_1.default {
|
|
13
|
-
async run() {
|
|
14
|
-
const { args: runArgs, flags: runFlags } = await this.parse(AddConnection);
|
|
15
|
-
const output = runFlags.target || runFlags.out || '.';
|
|
16
|
-
const isDir = fs_1.default.lstatSync(output).isDirectory();
|
|
17
|
-
this.target = isDir ? `${output}/config.json` : output;
|
|
18
|
-
const { connectionUrlOrPath } = runArgs;
|
|
19
|
-
const { assemblyNames, type, name, config, connectionId } = runFlags;
|
|
20
|
-
const { skipCheck, force } = runFlags;
|
|
21
|
-
const url = await this.resolveURL(connectionUrlOrPath, !(skipCheck || force));
|
|
22
|
-
const configContents = await this.readJsonFile(this.target);
|
|
23
|
-
this.debug(`Using config file ${this.target}`);
|
|
24
|
-
if (!configContents.assemblies?.length) {
|
|
25
|
-
this.error('No assemblies found. Please add one before adding connections', { exit: 120 });
|
|
26
|
-
}
|
|
27
|
-
const configType = type || this.determineConnectionType(url);
|
|
28
|
-
const id = connectionId ||
|
|
29
|
-
[configType, assemblyNames, +Date.now()].filter(f => !!f).join('-');
|
|
30
|
-
const connectionConfig = {
|
|
31
|
-
type: configType,
|
|
32
|
-
name: name || id,
|
|
33
|
-
...(configType === 'UCSCTrackHubConnection'
|
|
34
|
-
? {
|
|
35
|
-
hubTxtLocation: {
|
|
36
|
-
uri: url,
|
|
37
|
-
locationType: 'UriLocation',
|
|
38
|
-
},
|
|
39
|
-
}
|
|
40
|
-
: {}),
|
|
41
|
-
...(configType === 'JBrowse1Connection'
|
|
42
|
-
? {
|
|
43
|
-
dataDirLocation: {
|
|
44
|
-
uri: url,
|
|
45
|
-
locationType: 'UriLocation',
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
: {}),
|
|
49
|
-
connectionId: id,
|
|
50
|
-
assemblyNames: assemblyNames
|
|
51
|
-
? assemblyNames.split(',')
|
|
52
|
-
: type === 'JBrowse1Connection'
|
|
53
|
-
? [configContents.assemblies[0]?.name]
|
|
54
|
-
: undefined,
|
|
55
|
-
...(config ? (0, json_parse_better_errors_1.default)(config) : {}),
|
|
56
|
-
};
|
|
57
|
-
if (!configContents.connections) {
|
|
58
|
-
configContents.connections = [];
|
|
59
|
-
}
|
|
60
|
-
const idx = configContents.connections.findIndex(c => c.connectionId === connectionId);
|
|
61
|
-
if (idx !== -1) {
|
|
62
|
-
if (runFlags.force || runFlags.overwrite) {
|
|
63
|
-
configContents.connections[idx] = connectionConfig;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
this.error(`Cannot add connection with id ${connectionId}, a connection with that id already exists.\nUse --overwrite if you would like to replace the existing connection`, { exit: 150 });
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
configContents.connections.push(connectionConfig);
|
|
71
|
-
}
|
|
72
|
-
this.debug(`Writing configuration to file ${this.target}`);
|
|
73
|
-
await this.writeJsonFile(this.target, configContents);
|
|
74
|
-
this.log(`${idx !== -1 ? 'Overwrote' : 'Added'} connection "${name}" ${idx !== -1 ? 'in' : 'to'} ${this.target}`);
|
|
75
|
-
}
|
|
76
|
-
async resolveURL(location, check = true) {
|
|
77
|
-
let locationUrl;
|
|
78
|
-
try {
|
|
79
|
-
locationUrl = new URL(location);
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
this.error('The location provided is not a valid URL', { exit: 160 });
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
if (check) {
|
|
86
|
-
const response = await (0, fetchWithProxy_1.default)(`${locationUrl}`, { method: 'HEAD' });
|
|
87
|
-
if (!response.ok) {
|
|
88
|
-
this.error(`Response returned with code ${response.status}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return locationUrl.href;
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
// ignore
|
|
95
|
-
this.error(`Unable to fetch from URL, ${error}`, { exit: 170 });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
determineConnectionType(url) {
|
|
99
|
-
if (path_1.default.basename(url) === 'hub.txt') {
|
|
100
|
-
return 'UCSCTrackHubConnection';
|
|
101
|
-
}
|
|
102
|
-
if (url.includes('jbrowse/data')) {
|
|
103
|
-
return 'JBrowse1Connection';
|
|
104
|
-
}
|
|
105
|
-
return 'custom';
|
|
106
|
-
}
|
|
107
|
-
isValidJSON(str) {
|
|
108
|
-
try {
|
|
109
|
-
JSON.parse(str);
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
AddConnection.description = 'Add a connection to a JBrowse 2 configuration';
|
|
118
|
-
AddConnection.examples = [
|
|
119
|
-
'$ jbrowse add-connection http://mysite.com/jbrowse/data/ -a hg19',
|
|
120
|
-
'$ jbrowse add-connection http://mysite.com/jbrowse/custom_data_folder/ --type JBrowse1Connection -a hg38',
|
|
121
|
-
'$ jbrowse add-connection http://mysite.com/path/to/hub.txt',
|
|
122
|
-
'$ jbrowse add-connection http://mysite.com/path/to/custom_hub_name.txt --type UCSCTrackHubConnection',
|
|
123
|
-
`$ jbrowse add-connection http://mysite.com/path/to/custom --type custom --config '{"uri":{"url":"https://mysite.com/path/to/custom"}, "locationType": "UriLocation"}' -a hg19`,
|
|
124
|
-
'$ jbrowse add-connection https://mysite.com/path/to/hub.txt --connectionId newId --name newName --target /path/to/jb2/installation/config.json',
|
|
125
|
-
];
|
|
126
|
-
AddConnection.args = {
|
|
127
|
-
connectionUrlOrPath: core_1.Args.string({
|
|
128
|
-
required: true,
|
|
129
|
-
description: 'URL of data directory\nFor hub file, usually called hub.txt\nFor JBrowse 1, location of JB1 data directory similar to http://mysite.com/jbrowse/data/ ',
|
|
130
|
-
}),
|
|
131
|
-
};
|
|
132
|
-
AddConnection.flags = {
|
|
133
|
-
type: core_1.Flags.string({
|
|
134
|
-
char: 't',
|
|
135
|
-
description: 'type of connection, ex. JBrowse1Connection, UCSCTrackHubConnection, custom',
|
|
136
|
-
}),
|
|
137
|
-
assemblyNames: core_1.Flags.string({
|
|
138
|
-
char: 'a',
|
|
139
|
-
description: 'For UCSC, optional: Comma separated list of assembly name(s) to filter from this connection. For JBrowse: a single assembly name',
|
|
140
|
-
}),
|
|
141
|
-
config: core_1.Flags.string({
|
|
142
|
-
char: 'c',
|
|
143
|
-
description: `Any extra config settings to add to connection in JSON object format, such as '{"uri":"url":"https://sample.com"}, "locationType": "UriLocation"}'`,
|
|
144
|
-
}),
|
|
145
|
-
connectionId: core_1.Flags.string({
|
|
146
|
-
description: `Id for the connection that must be unique to JBrowse. Defaults to 'connectionType-assemblyName-currentTime'`,
|
|
147
|
-
}),
|
|
148
|
-
name: core_1.Flags.string({
|
|
149
|
-
char: 'n',
|
|
150
|
-
description: 'Name of the connection. Defaults to connectionId if not provided',
|
|
151
|
-
}),
|
|
152
|
-
target: core_1.Flags.string({
|
|
153
|
-
description: 'path to config file in JB2 installation directory to write out to.',
|
|
154
|
-
}),
|
|
155
|
-
out: core_1.Flags.string({
|
|
156
|
-
description: 'synonym for target',
|
|
157
|
-
}),
|
|
158
|
-
help: core_1.Flags.help({ char: 'h' }),
|
|
159
|
-
skipCheck: core_1.Flags.boolean({
|
|
160
|
-
description: "Don't check whether or not the data directory URL exists or if you are in a JBrowse directory",
|
|
161
|
-
}),
|
|
162
|
-
overwrite: core_1.Flags.boolean({
|
|
163
|
-
description: 'Overwrites any existing connections if same connection id',
|
|
164
|
-
}),
|
|
165
|
-
force: core_1.Flags.boolean({
|
|
166
|
-
char: 'f',
|
|
167
|
-
description: 'Equivalent to `--skipCheck --overwrite`',
|
|
168
|
-
}),
|
|
169
|
-
};
|
|
170
|
-
exports.default = AddConnection;
|