@jbrowse/plugin-gtf 2.13.1 → 2.14.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/dist/GtfAdapter/GtfAdapter.d.ts +7 -5
- package/dist/GtfAdapter/GtfAdapter.js +73 -61
- package/dist/util.d.ts +1 -1
- package/dist/util.js +12 -13
- package/esm/GtfAdapter/GtfAdapter.d.ts +7 -5
- package/esm/GtfAdapter/GtfAdapter.js +74 -62
- package/esm/util.d.ts +1 -1
- package/esm/util.js +12 -13
- package/package.json +3 -3
|
@@ -2,16 +2,18 @@ import { BaseFeatureDataAdapter, BaseOptions } from '@jbrowse/core/data_adapters
|
|
|
2
2
|
import { NoAssemblyRegion } from '@jbrowse/core/util/types';
|
|
3
3
|
import IntervalTree from '@flatten-js/interval-tree';
|
|
4
4
|
import { Feature } from '@jbrowse/core/util';
|
|
5
|
+
type StatusCallback = (arg: string) => void;
|
|
5
6
|
export default class GtfAdapter extends BaseFeatureDataAdapter {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
calculatedIntervalTreeMap: Record<string, IntervalTree>;
|
|
8
|
+
gtfFeatures?: Promise<{
|
|
9
|
+
header: string;
|
|
10
|
+
intervalTreeMap: Record<string, (sc?: StatusCallback) => IntervalTree>;
|
|
8
11
|
}>;
|
|
9
|
-
protected intervalTrees: Record<string, Promise<IntervalTree | undefined> | undefined>;
|
|
10
12
|
private loadDataP;
|
|
11
13
|
private loadData;
|
|
12
14
|
getRefNames(opts?: BaseOptions): Promise<string[]>;
|
|
13
|
-
|
|
14
|
-
private loadFeatureIntervalTree;
|
|
15
|
+
getHeader(opts?: BaseOptions): Promise<string>;
|
|
15
16
|
getFeatures(query: NoAssemblyRegion, opts?: BaseOptions): import("rxjs").Observable<Feature>;
|
|
16
17
|
freeResources(): void;
|
|
17
18
|
}
|
|
19
|
+
export {};
|
|
@@ -12,43 +12,79 @@ const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
|
|
|
12
12
|
const gtf_1 = __importDefault(require("@gmod/gtf"));
|
|
13
13
|
// locals
|
|
14
14
|
const util_2 = require("../util");
|
|
15
|
-
|
|
16
|
-
return buf[0] === 31 && buf[1] === 139 && buf[2] === 8;
|
|
17
|
-
}
|
|
15
|
+
const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
|
|
18
16
|
class GtfAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
19
17
|
constructor() {
|
|
20
18
|
super(...arguments);
|
|
21
|
-
this.
|
|
19
|
+
this.calculatedIntervalTreeMap = {};
|
|
22
20
|
}
|
|
23
|
-
async loadDataP(opts
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
async loadDataP(opts) {
|
|
22
|
+
const { statusCallback = () => { } } = opts || {};
|
|
23
|
+
const buf = (await (0, io_1.openLocation)(this.getConf('gtfLocation'), this.pluginManager).readFile(opts));
|
|
24
|
+
const buffer = (0, util_1.isGzip)(buf)
|
|
25
|
+
? await (0, util_1.updateStatus)('Unzipping', statusCallback, () => (0, bgzf_filehandle_1.unzip)(buf))
|
|
26
|
+
: buf;
|
|
27
|
+
const headerLines = [];
|
|
28
|
+
const featureMap = {};
|
|
29
|
+
let blockStart = 0;
|
|
30
|
+
let i = 0;
|
|
31
|
+
while (blockStart < buffer.length) {
|
|
32
|
+
const n = buffer.indexOf('\n', blockStart);
|
|
33
|
+
// could be a non-newline ended file, so slice to end of file if n===-1
|
|
34
|
+
const b = n === -1 ? buffer.slice(blockStart) : buffer.slice(blockStart, n);
|
|
35
|
+
const line = ((decoder === null || decoder === void 0 ? void 0 : decoder.decode(b)) || b.toString()).trim();
|
|
36
|
+
if (line) {
|
|
37
|
+
if (line.startsWith('#')) {
|
|
38
|
+
headerLines.push(line);
|
|
39
|
+
}
|
|
40
|
+
else if (line.startsWith('>')) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const ret = line.indexOf('\t');
|
|
45
|
+
const refName = line.slice(0, ret);
|
|
46
|
+
if (!featureMap[refName]) {
|
|
47
|
+
featureMap[refName] = '';
|
|
48
|
+
}
|
|
49
|
+
featureMap[refName] += `${line}\n`;
|
|
50
|
+
}
|
|
39
51
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!feats[refName]) {
|
|
43
|
-
feats[refName] = [];
|
|
52
|
+
if (i++ % 10000 === 0) {
|
|
53
|
+
statusCallback(`Loading ${Math.floor(blockStart / 1000000).toLocaleString('en-US')}/${Math.floor(buffer.length / 1000000).toLocaleString('en-US')} MB`);
|
|
44
54
|
}
|
|
45
|
-
|
|
55
|
+
blockStart = n + 1;
|
|
46
56
|
}
|
|
47
|
-
|
|
57
|
+
const intervalTreeMap = Object.fromEntries(Object.entries(featureMap).map(([refName, lines]) => [
|
|
58
|
+
refName,
|
|
59
|
+
(sc) => {
|
|
60
|
+
if (!this.calculatedIntervalTreeMap[refName]) {
|
|
61
|
+
sc === null || sc === void 0 ? void 0 : sc('Parsing GTF data');
|
|
62
|
+
const intervalTree = new interval_tree_1.default();
|
|
63
|
+
gtf_1.default.parseStringSync(lines, {
|
|
64
|
+
parseFeatures: true,
|
|
65
|
+
parseComments: false,
|
|
66
|
+
parseDirectives: false,
|
|
67
|
+
parseSequences: false,
|
|
68
|
+
})
|
|
69
|
+
.flat()
|
|
70
|
+
.map((f, i) => new util_1.SimpleFeature({
|
|
71
|
+
data: (0, util_2.featureData)(f),
|
|
72
|
+
id: `${this.id}-${refName}-${i}`,
|
|
73
|
+
}))
|
|
74
|
+
.forEach(obj => intervalTree.insert([obj.get('start'), obj.get('end')], obj));
|
|
75
|
+
this.calculatedIntervalTreeMap[refName] = intervalTree;
|
|
76
|
+
}
|
|
77
|
+
return this.calculatedIntervalTreeMap[refName];
|
|
78
|
+
},
|
|
79
|
+
]));
|
|
80
|
+
return {
|
|
81
|
+
header: headerLines.join('\n'),
|
|
82
|
+
intervalTreeMap,
|
|
83
|
+
};
|
|
48
84
|
}
|
|
49
85
|
async loadData(opts = {}) {
|
|
50
86
|
if (!this.gtfFeatures) {
|
|
51
|
-
this.gtfFeatures = this.loadDataP(opts).catch(e => {
|
|
87
|
+
this.gtfFeatures = this.loadDataP(opts).catch((e) => {
|
|
52
88
|
this.gtfFeatures = undefined;
|
|
53
89
|
throw e;
|
|
54
90
|
});
|
|
@@ -56,46 +92,22 @@ class GtfAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
56
92
|
return this.gtfFeatures;
|
|
57
93
|
}
|
|
58
94
|
async getRefNames(opts = {}) {
|
|
59
|
-
const {
|
|
60
|
-
return Object.keys(
|
|
61
|
-
}
|
|
62
|
-
async loadFeatureIntervalTreeHelper(refName) {
|
|
63
|
-
const { feats } = await this.loadData();
|
|
64
|
-
const lines = feats[refName];
|
|
65
|
-
if (!lines) {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
const data = gtf_1.default.parseStringSync(lines.join('\n'), {
|
|
69
|
-
parseFeatures: true,
|
|
70
|
-
parseComments: false,
|
|
71
|
-
parseDirectives: false,
|
|
72
|
-
parseSequences: false,
|
|
73
|
-
});
|
|
74
|
-
const intervalTree = new interval_tree_1.default();
|
|
75
|
-
const ret = data.flat().map((f, i) => new util_1.SimpleFeature({
|
|
76
|
-
data: (0, util_2.featureData)(f),
|
|
77
|
-
id: `${this.id}-${refName}-${i}`,
|
|
78
|
-
}));
|
|
79
|
-
for (const obj of ret) {
|
|
80
|
-
intervalTree.insert([obj.get('start'), obj.get('end')], obj);
|
|
81
|
-
}
|
|
82
|
-
return intervalTree;
|
|
95
|
+
const { intervalTreeMap } = await this.loadData(opts);
|
|
96
|
+
return Object.keys(intervalTreeMap);
|
|
83
97
|
}
|
|
84
|
-
async
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.intervalTrees[refName] = undefined;
|
|
88
|
-
throw e;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
return this.intervalTrees[refName];
|
|
98
|
+
async getHeader(opts = {}) {
|
|
99
|
+
const { header } = await this.loadData(opts);
|
|
100
|
+
return header;
|
|
92
101
|
}
|
|
93
102
|
getFeatures(query, opts = {}) {
|
|
94
103
|
return (0, rxjs_1.ObservableCreate)(async (observer) => {
|
|
104
|
+
var _a;
|
|
95
105
|
try {
|
|
96
106
|
const { start, end, refName } = query;
|
|
97
|
-
const
|
|
98
|
-
|
|
107
|
+
const { intervalTreeMap } = await this.loadData(opts);
|
|
108
|
+
(_a = intervalTreeMap[refName]) === null || _a === void 0 ? void 0 : _a.call(intervalTreeMap, opts.statusCallback).search([start, end]).forEach(f => {
|
|
109
|
+
observer.next(f);
|
|
110
|
+
});
|
|
99
111
|
observer.complete();
|
|
100
112
|
}
|
|
101
113
|
catch (e) {
|
package/dist/util.d.ts
CHANGED
package/dist/util.js
CHANGED
|
@@ -8,10 +8,10 @@ function featureData(data) {
|
|
|
8
8
|
f.phase = Number(data.frame);
|
|
9
9
|
f.refName = data.seq_name;
|
|
10
10
|
if (data.score === null) {
|
|
11
|
-
|
|
11
|
+
f.score = undefined;
|
|
12
12
|
}
|
|
13
13
|
if (data.frame === null) {
|
|
14
|
-
|
|
14
|
+
f.score = undefined;
|
|
15
15
|
}
|
|
16
16
|
const defaultFields = new Set([
|
|
17
17
|
'start',
|
|
@@ -30,12 +30,12 @@ function featureData(data) {
|
|
|
30
30
|
// reproduces behavior of NCList
|
|
31
31
|
b += '2';
|
|
32
32
|
}
|
|
33
|
-
if (data.attributes[a]
|
|
33
|
+
if (data.attributes[a]) {
|
|
34
34
|
let attr = data.attributes[a];
|
|
35
35
|
if (Array.isArray(attr) && attr.length === 1) {
|
|
36
36
|
// gtf uses double quotes for text values in the attributes column,
|
|
37
37
|
// remove them
|
|
38
|
-
attr =
|
|
38
|
+
attr = attr[0].replaceAll(/^"|"$/g, '');
|
|
39
39
|
}
|
|
40
40
|
f[b] = attr;
|
|
41
41
|
}
|
|
@@ -46,15 +46,14 @@ function featureData(data) {
|
|
|
46
46
|
if (data.child_features && data.child_features.length > 0) {
|
|
47
47
|
f.subfeatures = data.child_features.flatMap(childLocs => childLocs.map(childLoc => featureData(childLoc)));
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
delete f.frame;
|
|
49
|
+
f.child_features = undefined;
|
|
50
|
+
f.data = undefined;
|
|
51
|
+
f.derived_features = undefined;
|
|
52
|
+
f._linehash = undefined;
|
|
53
|
+
f.attributes = undefined;
|
|
54
|
+
f.seq_name = undefined;
|
|
55
|
+
f.featureType = undefined;
|
|
56
|
+
f.frame = undefined;
|
|
58
57
|
if (f.transcript_id) {
|
|
59
58
|
f.name = f.transcript_id;
|
|
60
59
|
}
|
|
@@ -2,16 +2,18 @@ import { BaseFeatureDataAdapter, BaseOptions } from '@jbrowse/core/data_adapters
|
|
|
2
2
|
import { NoAssemblyRegion } from '@jbrowse/core/util/types';
|
|
3
3
|
import IntervalTree from '@flatten-js/interval-tree';
|
|
4
4
|
import { Feature } from '@jbrowse/core/util';
|
|
5
|
+
type StatusCallback = (arg: string) => void;
|
|
5
6
|
export default class GtfAdapter extends BaseFeatureDataAdapter {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
calculatedIntervalTreeMap: Record<string, IntervalTree>;
|
|
8
|
+
gtfFeatures?: Promise<{
|
|
9
|
+
header: string;
|
|
10
|
+
intervalTreeMap: Record<string, (sc?: StatusCallback) => IntervalTree>;
|
|
8
11
|
}>;
|
|
9
|
-
protected intervalTrees: Record<string, Promise<IntervalTree | undefined> | undefined>;
|
|
10
12
|
private loadDataP;
|
|
11
13
|
private loadData;
|
|
12
14
|
getRefNames(opts?: BaseOptions): Promise<string[]>;
|
|
13
|
-
|
|
14
|
-
private loadFeatureIntervalTree;
|
|
15
|
+
getHeader(opts?: BaseOptions): Promise<string>;
|
|
15
16
|
getFeatures(query: NoAssemblyRegion, opts?: BaseOptions): import("rxjs").Observable<Feature>;
|
|
16
17
|
freeResources(): void;
|
|
17
18
|
}
|
|
19
|
+
export {};
|
|
@@ -2,48 +2,84 @@ import { BaseFeatureDataAdapter, } from '@jbrowse/core/data_adapters/BaseAdapter
|
|
|
2
2
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
3
3
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
4
4
|
import IntervalTree from '@flatten-js/interval-tree';
|
|
5
|
-
import { SimpleFeature } from '@jbrowse/core/util';
|
|
5
|
+
import { SimpleFeature, updateStatus, isGzip, } from '@jbrowse/core/util';
|
|
6
6
|
import { unzip } from '@gmod/bgzf-filehandle';
|
|
7
7
|
import gtf from '@gmod/gtf';
|
|
8
8
|
// locals
|
|
9
9
|
import { featureData } from '../util';
|
|
10
|
-
|
|
11
|
-
return buf[0] === 31 && buf[1] === 139 && buf[2] === 8;
|
|
12
|
-
}
|
|
10
|
+
const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
|
|
13
11
|
export default class GtfAdapter extends BaseFeatureDataAdapter {
|
|
14
12
|
constructor() {
|
|
15
13
|
super(...arguments);
|
|
16
|
-
this.
|
|
14
|
+
this.calculatedIntervalTreeMap = {};
|
|
17
15
|
}
|
|
18
|
-
async loadDataP(opts
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
async loadDataP(opts) {
|
|
17
|
+
const { statusCallback = () => { } } = opts || {};
|
|
18
|
+
const buf = (await openLocation(this.getConf('gtfLocation'), this.pluginManager).readFile(opts));
|
|
19
|
+
const buffer = isGzip(buf)
|
|
20
|
+
? await updateStatus('Unzipping', statusCallback, () => unzip(buf))
|
|
21
|
+
: buf;
|
|
22
|
+
const headerLines = [];
|
|
23
|
+
const featureMap = {};
|
|
24
|
+
let blockStart = 0;
|
|
25
|
+
let i = 0;
|
|
26
|
+
while (blockStart < buffer.length) {
|
|
27
|
+
const n = buffer.indexOf('\n', blockStart);
|
|
28
|
+
// could be a non-newline ended file, so slice to end of file if n===-1
|
|
29
|
+
const b = n === -1 ? buffer.slice(blockStart) : buffer.slice(blockStart, n);
|
|
30
|
+
const line = ((decoder === null || decoder === void 0 ? void 0 : decoder.decode(b)) || b.toString()).trim();
|
|
31
|
+
if (line) {
|
|
32
|
+
if (line.startsWith('#')) {
|
|
33
|
+
headerLines.push(line);
|
|
34
|
+
}
|
|
35
|
+
else if (line.startsWith('>')) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const ret = line.indexOf('\t');
|
|
40
|
+
const refName = line.slice(0, ret);
|
|
41
|
+
if (!featureMap[refName]) {
|
|
42
|
+
featureMap[refName] = '';
|
|
43
|
+
}
|
|
44
|
+
featureMap[refName] += `${line}\n`;
|
|
45
|
+
}
|
|
34
46
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!feats[refName]) {
|
|
38
|
-
feats[refName] = [];
|
|
47
|
+
if (i++ % 10000 === 0) {
|
|
48
|
+
statusCallback(`Loading ${Math.floor(blockStart / 1000000).toLocaleString('en-US')}/${Math.floor(buffer.length / 1000000).toLocaleString('en-US')} MB`);
|
|
39
49
|
}
|
|
40
|
-
|
|
50
|
+
blockStart = n + 1;
|
|
41
51
|
}
|
|
42
|
-
|
|
52
|
+
const intervalTreeMap = Object.fromEntries(Object.entries(featureMap).map(([refName, lines]) => [
|
|
53
|
+
refName,
|
|
54
|
+
(sc) => {
|
|
55
|
+
if (!this.calculatedIntervalTreeMap[refName]) {
|
|
56
|
+
sc === null || sc === void 0 ? void 0 : sc('Parsing GTF data');
|
|
57
|
+
const intervalTree = new IntervalTree();
|
|
58
|
+
gtf.parseStringSync(lines, {
|
|
59
|
+
parseFeatures: true,
|
|
60
|
+
parseComments: false,
|
|
61
|
+
parseDirectives: false,
|
|
62
|
+
parseSequences: false,
|
|
63
|
+
})
|
|
64
|
+
.flat()
|
|
65
|
+
.map((f, i) => new SimpleFeature({
|
|
66
|
+
data: featureData(f),
|
|
67
|
+
id: `${this.id}-${refName}-${i}`,
|
|
68
|
+
}))
|
|
69
|
+
.forEach(obj => intervalTree.insert([obj.get('start'), obj.get('end')], obj));
|
|
70
|
+
this.calculatedIntervalTreeMap[refName] = intervalTree;
|
|
71
|
+
}
|
|
72
|
+
return this.calculatedIntervalTreeMap[refName];
|
|
73
|
+
},
|
|
74
|
+
]));
|
|
75
|
+
return {
|
|
76
|
+
header: headerLines.join('\n'),
|
|
77
|
+
intervalTreeMap,
|
|
78
|
+
};
|
|
43
79
|
}
|
|
44
80
|
async loadData(opts = {}) {
|
|
45
81
|
if (!this.gtfFeatures) {
|
|
46
|
-
this.gtfFeatures = this.loadDataP(opts).catch(e => {
|
|
82
|
+
this.gtfFeatures = this.loadDataP(opts).catch((e) => {
|
|
47
83
|
this.gtfFeatures = undefined;
|
|
48
84
|
throw e;
|
|
49
85
|
});
|
|
@@ -51,46 +87,22 @@ export default class GtfAdapter extends BaseFeatureDataAdapter {
|
|
|
51
87
|
return this.gtfFeatures;
|
|
52
88
|
}
|
|
53
89
|
async getRefNames(opts = {}) {
|
|
54
|
-
const {
|
|
55
|
-
return Object.keys(
|
|
56
|
-
}
|
|
57
|
-
async loadFeatureIntervalTreeHelper(refName) {
|
|
58
|
-
const { feats } = await this.loadData();
|
|
59
|
-
const lines = feats[refName];
|
|
60
|
-
if (!lines) {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
const data = gtf.parseStringSync(lines.join('\n'), {
|
|
64
|
-
parseFeatures: true,
|
|
65
|
-
parseComments: false,
|
|
66
|
-
parseDirectives: false,
|
|
67
|
-
parseSequences: false,
|
|
68
|
-
});
|
|
69
|
-
const intervalTree = new IntervalTree();
|
|
70
|
-
const ret = data.flat().map((f, i) => new SimpleFeature({
|
|
71
|
-
data: featureData(f),
|
|
72
|
-
id: `${this.id}-${refName}-${i}`,
|
|
73
|
-
}));
|
|
74
|
-
for (const obj of ret) {
|
|
75
|
-
intervalTree.insert([obj.get('start'), obj.get('end')], obj);
|
|
76
|
-
}
|
|
77
|
-
return intervalTree;
|
|
90
|
+
const { intervalTreeMap } = await this.loadData(opts);
|
|
91
|
+
return Object.keys(intervalTreeMap);
|
|
78
92
|
}
|
|
79
|
-
async
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.intervalTrees[refName] = undefined;
|
|
83
|
-
throw e;
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
return this.intervalTrees[refName];
|
|
93
|
+
async getHeader(opts = {}) {
|
|
94
|
+
const { header } = await this.loadData(opts);
|
|
95
|
+
return header;
|
|
87
96
|
}
|
|
88
97
|
getFeatures(query, opts = {}) {
|
|
89
98
|
return ObservableCreate(async (observer) => {
|
|
99
|
+
var _a;
|
|
90
100
|
try {
|
|
91
101
|
const { start, end, refName } = query;
|
|
92
|
-
const
|
|
93
|
-
|
|
102
|
+
const { intervalTreeMap } = await this.loadData(opts);
|
|
103
|
+
(_a = intervalTreeMap[refName]) === null || _a === void 0 ? void 0 : _a.call(intervalTreeMap, opts.statusCallback).search([start, end]).forEach(f => {
|
|
104
|
+
observer.next(f);
|
|
105
|
+
});
|
|
94
106
|
observer.complete();
|
|
95
107
|
}
|
|
96
108
|
catch (e) {
|
package/esm/util.d.ts
CHANGED
package/esm/util.js
CHANGED
|
@@ -5,10 +5,10 @@ export function featureData(data) {
|
|
|
5
5
|
f.phase = Number(data.frame);
|
|
6
6
|
f.refName = data.seq_name;
|
|
7
7
|
if (data.score === null) {
|
|
8
|
-
|
|
8
|
+
f.score = undefined;
|
|
9
9
|
}
|
|
10
10
|
if (data.frame === null) {
|
|
11
|
-
|
|
11
|
+
f.score = undefined;
|
|
12
12
|
}
|
|
13
13
|
const defaultFields = new Set([
|
|
14
14
|
'start',
|
|
@@ -27,12 +27,12 @@ export function featureData(data) {
|
|
|
27
27
|
// reproduces behavior of NCList
|
|
28
28
|
b += '2';
|
|
29
29
|
}
|
|
30
|
-
if (data.attributes[a]
|
|
30
|
+
if (data.attributes[a]) {
|
|
31
31
|
let attr = data.attributes[a];
|
|
32
32
|
if (Array.isArray(attr) && attr.length === 1) {
|
|
33
33
|
// gtf uses double quotes for text values in the attributes column,
|
|
34
34
|
// remove them
|
|
35
|
-
attr =
|
|
35
|
+
attr = attr[0].replaceAll(/^"|"$/g, '');
|
|
36
36
|
}
|
|
37
37
|
f[b] = attr;
|
|
38
38
|
}
|
|
@@ -43,15 +43,14 @@ export function featureData(data) {
|
|
|
43
43
|
if (data.child_features && data.child_features.length > 0) {
|
|
44
44
|
f.subfeatures = data.child_features.flatMap(childLocs => childLocs.map(childLoc => featureData(childLoc)));
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
delete f.frame;
|
|
46
|
+
f.child_features = undefined;
|
|
47
|
+
f.data = undefined;
|
|
48
|
+
f.derived_features = undefined;
|
|
49
|
+
f._linehash = undefined;
|
|
50
|
+
f.attributes = undefined;
|
|
51
|
+
f.seq_name = undefined;
|
|
52
|
+
f.featureType = undefined;
|
|
53
|
+
f.frame = undefined;
|
|
55
54
|
if (f.transcript_id) {
|
|
56
55
|
f.name = f.transcript_id;
|
|
57
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-gtf",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "JBrowse 2 gtf feature adapter",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "npm-run-all build:*",
|
|
27
|
-
"test": "cd ../..; jest plugins/gtf --passWithNoTests",
|
|
27
|
+
"test": "cd ../..; jest --passWithNoTests plugins/gtf --passWithNoTests",
|
|
28
28
|
"prepublishOnly": "yarn test",
|
|
29
29
|
"prepack": "yarn build && yarn useDist",
|
|
30
30
|
"postpack": "yarn useSrc",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"distModule": "esm/index.js",
|
|
57
57
|
"srcModule": "src/index.ts",
|
|
58
58
|
"module": "esm/index.js",
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "9fb8231d932db40adf0a283081765431756c66ff"
|
|
60
60
|
}
|