@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.
@@ -1,153 +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 crypto_1 = __importDefault(require("crypto"));
7
- const fs_1 = __importDefault(require("fs"));
8
- const os_1 = __importDefault(require("os"));
9
- const path_1 = __importDefault(require("path"));
10
- const core_1 = require("@oclif/core");
11
- const boxen_1 = __importDefault(require("boxen"));
12
- const chalk_1 = __importDefault(require("chalk"));
13
- const cors_1 = __importDefault(require("cors"));
14
- const express_1 = __importDefault(require("express"));
15
- const base_1 = __importDefault(require("../base"));
16
- function isValidPort(port) {
17
- return port > 0 && port < 65535;
18
- }
19
- // generate a string of random alphanumeric characters to serve as admin key
20
- function generateKey() {
21
- return crypto_1.default.randomBytes(5).toString('hex');
22
- }
23
- class AdminServer extends base_1.default {
24
- async run() {
25
- const { flags: runFlags } = await this.parse(AdminServer);
26
- const { root, bodySizeLimit } = runFlags;
27
- const output = root || '.';
28
- const isDir = fs_1.default.lstatSync(output).isDirectory();
29
- const outFile = isDir ? `${output}/config.json` : output;
30
- const baseDir = path_1.default.dirname(outFile);
31
- if (fs_1.default.existsSync(outFile)) {
32
- this.debug(`Found existing config file ${outFile}`);
33
- }
34
- else {
35
- this.debug(`Creating config file ${outFile}`);
36
- await this.writeJsonFile(outFile, {
37
- assemblies: [],
38
- configuration: {},
39
- connections: [],
40
- defaultSession: {
41
- name: 'New Session',
42
- },
43
- tracks: [],
44
- });
45
- }
46
- // start server with admin key in URL query string
47
- let port = 9090;
48
- if (runFlags.port) {
49
- if (!isValidPort(Number.parseInt(runFlags.port, 10))) {
50
- this.error(`${runFlags.port} is not a valid port`);
51
- }
52
- else {
53
- port = Number.parseInt(runFlags.port, 10);
54
- }
55
- }
56
- const app = (0, express_1.default)();
57
- app.use(express_1.default.static(baseDir));
58
- app.use((0, cors_1.default)());
59
- // POST route to save config
60
- app.use(express_1.default.json({ limit: bodySizeLimit }));
61
- app.post('/updateConfig', async (req, res) => {
62
- if (adminKey === req.body.adminKey) {
63
- this.debug('Admin key matches');
64
- try {
65
- // use directory traversal prevention
66
- // https://nodejs.org/en/knowledge/file-system/security/introduction/#preventing-directory-traversal
67
- const filename = req.body.configPath
68
- ? path_1.default.join(baseDir, req.body.configPath)
69
- : outFile;
70
- if (!filename.startsWith(baseDir)) {
71
- throw new Error(`Cannot perform directory traversal outside of ${baseDir}`);
72
- }
73
- await this.writeJsonFile(filename, req.body.config);
74
- res.send('Config written to disk');
75
- }
76
- catch (e) {
77
- res.status(500).send(`Could not write config file ${e}`);
78
- }
79
- }
80
- else {
81
- res.status(403).send('Admin key does not match');
82
- }
83
- });
84
- app.post('/shutdown', async (req, res) => {
85
- this.debug('Req body: ', req.body);
86
- if (req.body.adminKey === adminKey) {
87
- this.debug('Admin key matches');
88
- res.send('Exiting');
89
- server.close();
90
- }
91
- else {
92
- res.status(403).send('Admin key does not match');
93
- }
94
- });
95
- const adminKey = generateKey();
96
- const server = app.listen(port);
97
- // Server message adapted from `serve`
98
- // https://github.com/vercel/serve/blob/f65ac293c20058f809769a4dbf4951acc21df6df/bin/serve.js
99
- const details = server.address();
100
- let localAddress = '';
101
- let networkAddress = '';
102
- if (typeof details === 'string') {
103
- localAddress = details;
104
- }
105
- else if (details && typeof details === 'object') {
106
- const address = details.address === '::' ? 'localhost' : details.address;
107
- const ip = getNetworkAddress();
108
- localAddress = `http://${address}:${details.port}?adminKey=${adminKey}`;
109
- if (ip) {
110
- networkAddress = `http://${ip}:${details.port}?adminKey=${adminKey}`;
111
- }
112
- }
113
- let message = chalk_1.default.green('Now serving JBrowse\nNavigate to the below URL to configure');
114
- if (localAddress) {
115
- const prefix = networkAddress ? '- ' : '';
116
- const space = networkAddress ? ' ' : ' ';
117
- message += `\n\n${chalk_1.default.bold(`${prefix}Local:`)}${space}${localAddress}`;
118
- }
119
- if (networkAddress) {
120
- message += `\n${chalk_1.default.bold('- On Your Network:')} ${networkAddress}`;
121
- }
122
- this.log((0, boxen_1.default)(message, { padding: 1, borderColor: 'blue', margin: 1 }));
123
- this.log(`If you are running yarn start you can launch http://localhost:3000?adminKey=${adminKey}&adminServer=http://localhost:${port}/updateConfig`);
124
- }
125
- }
126
- AdminServer.description = 'Start up a small admin server for JBrowse configuration';
127
- AdminServer.examples = ['$ jbrowse admin-server', '$ jbrowse admin-server -p 8888'];
128
- AdminServer.flags = {
129
- port: core_1.Flags.string({
130
- char: 'p',
131
- description: 'Specifified port to start the server on;\nDefault is 9090.',
132
- }),
133
- root: core_1.Flags.string({
134
- description: 'path to the root of the JB2 installation.\nCreates ./config.json if nonexistent. note that you can navigate to ?config=path/to/subconfig.json in the web browser and it will write to rootDir/path/to/subconfig.json',
135
- }),
136
- bodySizeLimit: core_1.Flags.string({
137
- description: 'Size limit of the update message; may need to increase if config is large.\nArgument is passed to bytes library for parsing: https://www.npmjs.com/package/bytes.',
138
- default: '25mb',
139
- }),
140
- help: core_1.Flags.help({ char: 'h' }),
141
- };
142
- exports.default = AdminServer;
143
- function getNetworkAddress() {
144
- for (const network of Object.values(os_1.default.networkInterfaces())) {
145
- for (const networkInterface of network || []) {
146
- const { address, family, internal } = networkInterface;
147
- if (family === 'IPv4' && !internal) {
148
- return address;
149
- }
150
- }
151
- }
152
- return undefined;
153
- }
@@ -1,111 +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 core_1 = require("@oclif/core");
8
- const decompress_1 = __importDefault(require("decompress"));
9
- const base_1 = __importDefault(require("../base"));
10
- const fetchWithProxy_1 = __importDefault(require("../fetchWithProxy"));
11
- const fsPromises = fs_1.default.promises;
12
- class Create extends base_1.default {
13
- async run() {
14
- const { args: runArgs, flags: runFlags } = await this.parse(Create);
15
- const { localPath: argsPath } = runArgs;
16
- this.debug(`Want to install path at: ${argsPath}`);
17
- const { force, url, listVersions, tag, branch, nightly } = runFlags;
18
- if (listVersions) {
19
- const versions = (await this.fetchGithubVersions()).map(version => version.tag_name);
20
- this.log(`All JBrowse versions:\n${versions.join('\n')}`);
21
- this.exit();
22
- }
23
- // mkdir will do nothing if dir exists
24
- await fsPromises.mkdir(argsPath, { recursive: true });
25
- if (!force) {
26
- await this.checkPath(argsPath);
27
- }
28
- const locationUrl = url ||
29
- (nightly ? await this.getBranch('main') : '') ||
30
- (branch ? await this.getBranch(branch) : '') ||
31
- (tag ? await this.getTag(tag) : await this.getLatest());
32
- this.log(`Fetching ${locationUrl}...`);
33
- const response = await (0, fetchWithProxy_1.default)(locationUrl);
34
- if (!response.ok) {
35
- this.error(`Failed to fetch: ${response.statusText}`, { exit: 100 });
36
- }
37
- const type = response.headers.get('content-type');
38
- if (url &&
39
- type !== 'application/zip' &&
40
- type !== 'application/octet-stream') {
41
- this.error('The URL provided does not seem to be a JBrowse installation URL');
42
- }
43
- await (0, decompress_1.default)(Buffer.from(await response.arrayBuffer()), argsPath);
44
- this.log(`Unpacked ${locationUrl} at ${argsPath}`);
45
- }
46
- async checkPath(userPath) {
47
- const allFiles = await fsPromises.readdir(userPath);
48
- if (allFiles.length > 0) {
49
- this.error(`This directory (${userPath}) has existing files and could cause conflicts with create. Please choose another directory or use the force flag to overwrite existing files`, { exit: 120 });
50
- }
51
- }
52
- async catch(error) {
53
- // @ts-expect-error
54
- if (error.parse?.output.flags.listVersions) {
55
- const versions = (await this.fetchGithubVersions()).map(version => version.tag_name);
56
- this.log(`All JBrowse versions:\n${versions.join('\n')}`);
57
- this.exit();
58
- }
59
- throw error;
60
- }
61
- }
62
- Create.description = 'Downloads and installs the latest JBrowse 2 release';
63
- Create.examples = [
64
- '# Download latest release from github, and put in specific path',
65
- '$ jbrowse create /path/to/new/installation',
66
- '',
67
- '# Download latest release from github and force overwrite existing contents at path',
68
- '$ jbrowse create /path/to/new/installation --force',
69
- '',
70
- '# Download latest release from a specific URL',
71
- '$ jbrowse create /path/to/new/installation --url url.com/directjbrowselink.zip',
72
- '',
73
- '# Download a specific tag from github',
74
- '$ jbrowse create /path/to/new/installation --tag v1.0.0',
75
- '',
76
- '# List available versions',
77
- '$ jbrowse create --listVersions',
78
- ];
79
- Create.args = {
80
- localPath: core_1.Args.string({
81
- required: true,
82
- description: 'Location where JBrowse 2 will be installed',
83
- }),
84
- };
85
- Create.flags = {
86
- help: core_1.Flags.help({ char: 'h' }),
87
- force: core_1.Flags.boolean({
88
- char: 'f',
89
- description: 'Overwrites existing JBrowse 2 installation if present in path',
90
- }),
91
- // will need to account for pagination once there is a lot of releases
92
- listVersions: core_1.Flags.boolean({
93
- char: 'l',
94
- description: 'Lists out all versions of JBrowse 2',
95
- }),
96
- branch: core_1.Flags.string({
97
- description: 'Download a development build from a named git branch',
98
- }),
99
- nightly: core_1.Flags.boolean({
100
- description: 'Download the latest development build from the main branch',
101
- }),
102
- url: core_1.Flags.string({
103
- char: 'u',
104
- description: 'A direct URL to a JBrowse 2 release',
105
- }),
106
- tag: core_1.Flags.string({
107
- char: 't',
108
- description: 'Version of JBrowse 2 to install. Format is v1.0.0.\nDefaults to latest',
109
- }),
110
- };
111
- exports.default = Create;
@@ -1,116 +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
- exports.flipCigar = flipCigar;
7
- exports.swapIndelCigar = swapIndelCigar;
8
- exports.createPIF = createPIF;
9
- const child_process_1 = require("child_process");
10
- const fs_1 = __importDefault(require("fs"));
11
- const path_1 = __importDefault(require("path"));
12
- const readline_1 = __importDefault(require("readline"));
13
- const zlib_1 = require("zlib");
14
- const core_1 = require("@oclif/core");
15
- const command_exists_1 = require("command-exists");
16
- const base_1 = __importDefault(require("../base"));
17
- const cigarRegex = new RegExp(/([MIDNSHPX=])/);
18
- function getReadline(filename) {
19
- const stream = fs_1.default.createReadStream(filename);
20
- return readline_1.default.createInterface({
21
- input: /.b?gz$/.exec(filename) ? stream.pipe((0, zlib_1.createGunzip)()) : stream,
22
- });
23
- }
24
- function getStdReadline() {
25
- return readline_1.default.createInterface({
26
- input: process.stdin,
27
- });
28
- }
29
- function parseCigar(cigar = '') {
30
- return cigar.split(cigarRegex).slice(0, -1);
31
- }
32
- function flipCigar(cigar) {
33
- const arr = [];
34
- for (let i = cigar.length - 2; i >= 0; i -= 2) {
35
- arr.push(cigar[i]);
36
- const op = cigar[i + 1];
37
- if (op === 'D') {
38
- arr.push('I');
39
- }
40
- else if (op === 'I') {
41
- arr.push('D');
42
- }
43
- else {
44
- arr.push(op);
45
- }
46
- }
47
- return arr;
48
- }
49
- function swapIndelCigar(cigar) {
50
- return cigar.replaceAll('D', 'K').replaceAll('I', 'D').replaceAll('K', 'I');
51
- }
52
- async function createPIF(filename, stream) {
53
- const rl1 = filename ? getReadline(filename) : getStdReadline();
54
- for await (const line of rl1) {
55
- const [c1, l1, s1, e1, strand, c2, l2, s2, e2, ...rest] = line.split('\t');
56
- stream.write(`${[`t${c2}`, l2, s2, e2, strand, c1, l1, s1, e1, ...rest].join('\t')}\n`);
57
- const cigarIdx = rest.findIndex(f => f.startsWith('cg:Z'));
58
- const CIGAR = rest[cigarIdx];
59
- if (CIGAR) {
60
- rest[cigarIdx] = `cg:Z:${strand === '-'
61
- ? flipCigar(parseCigar(CIGAR.slice(5))).join('')
62
- : swapIndelCigar(CIGAR.slice(5))}`;
63
- }
64
- stream.write(`${[`q${c1}`, l1, s1, e1, strand, c2, l2, s2, e2, ...rest].join('\t')}\n`);
65
- }
66
- rl1.close();
67
- }
68
- class MakePIF extends base_1.default {
69
- async run() {
70
- const { args: { file }, flags: { out, csi }, } = await this.parse(MakePIF);
71
- if ((0, command_exists_1.sync)('sh') &&
72
- (0, command_exists_1.sync)('sort') &&
73
- (0, command_exists_1.sync)('grep') &&
74
- (0, command_exists_1.sync)('tabix') &&
75
- (0, command_exists_1.sync)('bgzip')) {
76
- const fn = out || `${path_1.default.basename(file || 'output', '.paf')}.pif.gz`;
77
- const child = (0, child_process_1.spawn)('sh', [
78
- '-c',
79
- `sort -t"\`printf '\t'\`" -k1,1 -k3,3n | bgzip > ${fn}; tabix ${csi ? '-C ' : ''}-s1 -b3 -e4 -0 ${fn}`,
80
- ], {
81
- env: { ...process.env, LC_ALL: 'C' },
82
- stdio: ['pipe', process.stdout, process.stderr],
83
- });
84
- await createPIF(file, child.stdin);
85
- child.stdin.end();
86
- await new Promise(resolve => {
87
- child.on('close', resolve);
88
- });
89
- }
90
- else {
91
- throw new Error('Unable to sort, requires unix type environment with sort, grep, bgzip, tabix');
92
- }
93
- }
94
- }
95
- MakePIF.description = 'creates pairwise indexed PAF (PIF), with bgzip and tabix';
96
- MakePIF.examples = [
97
- '$ jbrowse pif input.paf # creates input.pif.gz in same directory',
98
- '',
99
- '$ jbrowse pif input.paf --out output.pif.gz # specify output file, creates output.pif.gz.tbi also',
100
- ];
101
- MakePIF.flags = {
102
- out: core_1.Flags.string({
103
- description: 'Where to write the output file. will write ${file}.pif.gz and ${file}.pif.gz.tbi',
104
- }),
105
- csi: core_1.Flags.boolean({
106
- description: 'Create a CSI index for the PIF file instead of TBI',
107
- }),
108
- help: core_1.Flags.help({ char: 'h' }),
109
- };
110
- MakePIF.args = {
111
- file: core_1.Args.string({
112
- required: true,
113
- description: 'PAF file as input',
114
- }),
115
- };
116
- exports.default = MakePIF;
@@ -1,37 +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 = require("fs");
7
- const core_1 = require("@oclif/core");
8
- const base_1 = __importDefault(require("../base"));
9
- class RemoveTrackJson extends base_1.default {
10
- async run() {
11
- const { args, flags: runFlags } = await this.parse(RemoveTrackJson);
12
- const output = runFlags.target || runFlags.out || '.';
13
- const isDir = (await fs_1.promises.lstat(output)).isDirectory();
14
- this.target = isDir ? `${output}/config.json` : output;
15
- const { track: inputId } = args;
16
- const config = await this.readJsonFile(this.target);
17
- config.tracks = config.tracks?.filter(({ trackId }) => trackId !== inputId);
18
- await this.writeJsonFile(this.target, config);
19
- }
20
- }
21
- RemoveTrackJson.description = 'Remove a track configuration from a JBrowse 2 configuration. Be aware that this can cause crashes in saved sessions that refer to this track!';
22
- RemoveTrackJson.examples = ['$ jbrowse remove-track-json trackId'];
23
- RemoveTrackJson.args = {
24
- track: core_1.Args.string({
25
- required: true,
26
- description: 'track JSON file or command line arg blob',
27
- }),
28
- };
29
- RemoveTrackJson.flags = {
30
- target: core_1.Flags.string({
31
- description: 'path to config file in JB2 installation directory to write out to.\nCreates ./config.json if nonexistent',
32
- }),
33
- out: core_1.Flags.string({
34
- description: 'synonym for target',
35
- }),
36
- };
37
- exports.default = RemoveTrackJson;
@@ -1,98 +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 core_1 = require("@oclif/core");
8
- const json_parse_better_errors_1 = __importDefault(require("json-parse-better-errors"));
9
- const base_1 = __importDefault(require("../base"));
10
- const fsPromises = fs_1.default.promises;
11
- class SetDefaultSession extends base_1.default {
12
- async run() {
13
- const { flags: runFlags } = await this.parse(SetDefaultSession);
14
- const { session, currentSession, delete: deleteDefaultSession } = runFlags;
15
- const output = runFlags.target || runFlags.out || '.';
16
- const isDir = (await fsPromises.lstat(output)).isDirectory();
17
- this.target = isDir ? `${output}/config.json` : output;
18
- const configContents = await this.readJsonFile(this.target);
19
- if (deleteDefaultSession) {
20
- configContents.defaultSession = undefined;
21
- await this.writeJsonFile(this.target, configContents);
22
- }
23
- else if (currentSession) {
24
- this.log(`The current default session is ${JSON.stringify(configContents.defaultSession)}`);
25
- this.exit();
26
- }
27
- else if (!session) {
28
- this.error('Please provide a --session file', { exit: 120 });
29
- }
30
- else if (session) {
31
- await this.writeJsonFile(this.target, {
32
- ...configContents,
33
- defaultSession: await this.readDefaultSessionFile(session),
34
- });
35
- }
36
- }
37
- async readDefaultSessionFile(defaultSessionFile) {
38
- let defaultSessionJson;
39
- try {
40
- defaultSessionJson = await fsPromises.readFile(defaultSessionFile, {
41
- encoding: 'utf8',
42
- });
43
- }
44
- catch (error) {
45
- return this.error('Could not read the provided file', { exit: 150 });
46
- }
47
- try {
48
- const session = (0, json_parse_better_errors_1.default)(defaultSessionJson);
49
- // return top-level "session" if it exists, such as in files created by
50
- // "File -> Export session"
51
- return session.session || session;
52
- }
53
- catch (error) {
54
- return this.error('Could not parse the given default session file', {
55
- exit: 160,
56
- });
57
- }
58
- }
59
- }
60
- SetDefaultSession.description = 'Set a default session with views and tracks';
61
- SetDefaultSession.examples = [
62
- '# set default session for the config.json in your current directory',
63
- '$ jbrowse set-default-session --session /path/to/default/session.json',
64
- '',
65
- '# make session.json the defaultSession on the specified target config.json file',
66
- '$ jbrowse set-default-session --target /path/to/jb2/installation/config.json --session session.json',
67
- '',
68
- '# print current default session',
69
- '$ jbrowse set-default-session --currentSession # Prints out current default session',
70
- ];
71
- SetDefaultSession.flags = {
72
- session: core_1.Flags.string({
73
- char: 's',
74
- description: 'set path to a file containing session in json format (required, unless using delete/currentSession flags)',
75
- }),
76
- name: core_1.Flags.string({
77
- char: 'n',
78
- description: 'Give a name for the default session',
79
- default: 'New Default Session',
80
- }),
81
- currentSession: core_1.Flags.boolean({
82
- char: 'c',
83
- description: 'List out the current default session',
84
- }),
85
- target: core_1.Flags.string({
86
- description: 'path to config file in JB2 installation directory to write out to',
87
- }),
88
- out: core_1.Flags.string({
89
- description: 'synonym for target',
90
- }),
91
- delete: core_1.Flags.boolean({
92
- description: 'Delete any existing default session.',
93
- }),
94
- help: core_1.Flags.help({
95
- char: 'h',
96
- }),
97
- };
98
- exports.default = SetDefaultSession;
@@ -1,45 +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 child_process_1 = require("child_process");
7
- const core_1 = require("@oclif/core");
8
- const command_exists_1 = require("command-exists");
9
- const base_1 = __importDefault(require("../base"));
10
- class SortGff extends base_1.default {
11
- async run() {
12
- const { args: { file }, } = await this.parse(SortGff);
13
- if ((0, command_exists_1.sync)('sh') &&
14
- (0, command_exists_1.sync)('sort') &&
15
- (0, command_exists_1.sync)('grep')) {
16
- // this command comes from the tabix docs http://www.htslib.org/doc/tabix.html
17
- (0, child_process_1.spawn)('sh', [
18
- '-c',
19
- `(grep "^#" "${file}"; grep -v "^#" "${file}" | sort -t"\`printf '\t'\`" -k1,1 -k2,2n)`,
20
- ], {
21
- env: { ...process.env, LC_ALL: 'C' },
22
- stdio: 'inherit',
23
- });
24
- }
25
- else {
26
- throw new Error('Unable to sort, requires unix type environment with sort, grep');
27
- }
28
- }
29
- }
30
- SortGff.description = 'Helper utility to sort GFF files for tabix. Moves all lines starting with # to the top of the file, and sort by refname and start position using unix utilities sort and grep';
31
- SortGff.examples = [
32
- '# sort gff and pipe to bgzip',
33
- '$ jbrowse sort-gff input.gff | bgzip > sorted.gff.gz',
34
- '$ tabix sorted.gff.gz',
35
- ];
36
- SortGff.args = {
37
- file: core_1.Args.string({
38
- required: true,
39
- description: 'GFF file',
40
- }),
41
- };
42
- SortGff.flags = {
43
- help: core_1.Flags.help({ char: 'h' }),
44
- };
45
- exports.default = SortGff;
@@ -1,45 +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 child_process_1 = require("child_process");
7
- const core_1 = require("@oclif/core");
8
- const command_exists_1 = require("command-exists");
9
- const base_1 = __importDefault(require("../base"));
10
- class SortGff extends base_1.default {
11
- async run() {
12
- const { args: { file }, } = await this.parse(SortGff);
13
- if ((0, command_exists_1.sync)('sh') &&
14
- (0, command_exists_1.sync)('sort') &&
15
- (0, command_exists_1.sync)('grep')) {
16
- // this command comes from the tabix docs http://www.htslib.org/doc/tabix.html
17
- (0, child_process_1.spawn)('sh', [
18
- '-c',
19
- `(grep "^#" "${file}"; grep -v "^#" "${file}" | sort -t"\`printf '\t'\`" -k1,1 -k4,4n)`,
20
- ], {
21
- env: { ...process.env, LC_ALL: 'C' },
22
- stdio: 'inherit',
23
- });
24
- }
25
- else {
26
- throw new Error('Unable to sort, requires unix type environment with sort, grep');
27
- }
28
- }
29
- }
30
- SortGff.description = 'Helper utility to sort GFF files for tabix. Moves all lines starting with # to the top of the file, and sort by refname and start position using unix utilities sort and grep';
31
- SortGff.examples = [
32
- '# sort gff and pipe to bgzip',
33
- '$ jbrowse sort-gff input.gff | bgzip > sorted.gff.gz',
34
- '$ tabix sorted.gff.gz',
35
- ];
36
- SortGff.args = {
37
- file: core_1.Args.string({
38
- required: true,
39
- description: 'GFF file',
40
- }),
41
- };
42
- SortGff.flags = {
43
- help: core_1.Flags.help({ char: 'h' }),
44
- };
45
- exports.default = SortGff;