@jbrowse/plugin-variants 1.5.0 → 1.5.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-variants",
3
- "version": "1.5.0",
3
+ "version": "1.5.4",
4
4
  "description": "JBrowse 2 variant adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -35,13 +35,13 @@
35
35
  "useSrc": "node ../../scripts/useSrc.js"
36
36
  },
37
37
  "dependencies": {
38
- "@flatten-js/interval-tree": "^1.0.14",
39
- "@gmod/bgzf-filehandle": "^1.3.4",
40
- "@gmod/tabix": "^1.5.0",
41
- "@gmod/vcf": "^5.0.0",
38
+ "@flatten-js/interval-tree": "^1.0.15",
39
+ "@gmod/bgzf-filehandle": "^1.4.2",
40
+ "@gmod/tabix": "^1.5.2",
41
+ "@gmod/vcf": "^5.0.4",
42
42
  "@material-ui/icons": "^4.11.2",
43
43
  "@mui/x-data-grid": "^4.0.1",
44
- "generic-filehandle": "^2.2.0"
44
+ "generic-filehandle": "^2.2.2"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@jbrowse/core": "^1.0.0",
@@ -58,5 +58,5 @@
58
58
  "publishConfig": {
59
59
  "access": "public"
60
60
  },
61
- "gitHead": "542025578a39bd170c8a166f2568ee7edbd54072"
61
+ "gitHead": "0c398214590969168694b4ed8e20b595178b9efd"
62
62
  }
@@ -1,10 +1,7 @@
1
1
  import BoxRendererType from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
2
2
  import Plugin from '@jbrowse/core/Plugin'
3
3
  import PluginManager from '@jbrowse/core/PluginManager'
4
- import PileupRenderer, {
5
- configSchema as pileupRendererConfigSchema,
6
- ReactComponent as PileupRendererReactComponent,
7
- } from '@jbrowse/plugin-alignments/src/PileupRenderer'
4
+ import PileupRenderer from '@jbrowse/plugin-alignments/src/PileupRenderer'
8
5
  import {
9
6
  configSchema as svgFeatureRendererConfigSchema,
10
7
  ReactComponent as SvgFeatureRendererReactComponent,
@@ -22,15 +19,7 @@ afterEach(() => {
22
19
 
23
20
  class PileupRendererPlugin extends Plugin {
24
21
  install(pluginManager) {
25
- pluginManager.addRendererType(
26
- () =>
27
- new PileupRenderer({
28
- name: 'PileupRenderer',
29
- ReactComponent: PileupRendererReactComponent,
30
- configSchema: pileupRendererConfigSchema,
31
- pluginManager,
32
- }),
33
- )
22
+ PileupRenderer(pluginManager)
34
23
  }
35
24
  }
36
25
 
@@ -225,7 +225,7 @@ function VariantFeatureDetails(props: any) {
225
225
  <BreakendPanel
226
226
  feature={feat}
227
227
  locStrings={feat.ALT.map(
228
- (alt: string) => parseBreakend(alt).MatePosition,
228
+ (alt: string) => parseBreakend(alt)?.MatePosition || '',
229
229
  )}
230
230
  model={model}
231
231
  />
@@ -182,6 +182,7 @@ exports[`VariantTrack widget renders with just the required model elements 1`] =
182
182
  >
183
183
  <div
184
184
  class="makeStyles-fieldName"
185
+ style="width: 62px;"
185
186
  >
186
187
  INFO.MQ
187
188
  </div>
@@ -2,135 +2,119 @@ import {
2
2
  BaseFeatureDataAdapter,
3
3
  BaseOptions,
4
4
  } from '@jbrowse/core/data_adapters/BaseAdapter'
5
- import { FileLocation, Region } from '@jbrowse/core/util/types'
5
+ import { Region } from '@jbrowse/core/util/types'
6
6
  import { openLocation } from '@jbrowse/core/util/io'
7
7
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
8
8
  import { Feature } from '@jbrowse/core/util/simpleFeature'
9
9
  import { readConfObject } from '@jbrowse/core/configuration'
10
- import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
11
10
  import IntervalTree from '@flatten-js/interval-tree'
12
- import VcfFeature from '../VcfTabixAdapter/VcfFeature'
13
- import VCF from '@gmod/vcf'
14
11
  import { unzip } from '@gmod/bgzf-filehandle'
15
- import PluginManager from '@jbrowse/core/PluginManager'
16
- import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
12
+ import VCF from '@gmod/vcf'
13
+ import VcfFeature from '../VcfTabixAdapter/VcfFeature'
17
14
 
18
15
  const readVcf = (f: string) => {
19
16
  const lines = f.split('\n')
20
17
  const header: string[] = []
21
- const refNames: string[] = []
22
18
  const rest: string[] = []
23
19
  lines.forEach(line => {
24
- if (line.startsWith('##contig')) {
25
- refNames.push(line.split('##contig=<ID=')[1].split(',')[0])
26
- } else if (line.startsWith('#')) {
20
+ if (line.startsWith('#')) {
27
21
  header.push(line)
28
22
  } else if (line) {
29
23
  rest.push(line)
30
24
  }
31
25
  })
32
- return { header: header.join('\n'), lines: rest, refNames }
26
+ return { header: header.join('\n'), lines: rest }
27
+ }
28
+
29
+ function isGzip(buf: Buffer) {
30
+ return buf[0] === 31 && buf[1] === 139 && buf[2] === 8
33
31
  }
34
32
 
35
33
  export default class VcfAdapter extends BaseFeatureDataAdapter {
36
34
  public static capabilities = ['getFeatures', 'getRefNames']
37
35
 
38
- protected vcfFeatures?: Promise<Record<string, IntervalTree>>
39
-
40
- public constructor(
41
- config: AnyConfigurationModel,
42
- getSubAdapter?: getSubAdapterType,
43
- pluginManager?: PluginManager,
44
- ) {
45
- super(config, getSubAdapter, pluginManager)
46
- }
47
-
48
- private async decodeFileContents() {
49
- const vcfLocation = readConfObject(
50
- this.config,
51
- 'vcfLocation',
52
- ) as FileLocation
53
-
54
- let fileContents = await openLocation(
55
- vcfLocation,
56
- this.pluginManager,
57
- ).readFile()
58
-
59
- if (
60
- typeof fileContents[0] === 'number' &&
61
- fileContents[0] === 31 &&
62
- typeof fileContents[1] === 'number' &&
63
- fileContents[1] === 139 &&
64
- typeof fileContents[2] === 'number' &&
65
- fileContents[2] === 8
66
- ) {
67
- fileContents = new TextDecoder().decode(await unzip(fileContents))
68
- } else {
69
- fileContents = fileContents.toString()
70
- }
71
-
72
- return readVcf(fileContents)
73
- }
36
+ protected vcfFeatures?: Promise<{
37
+ header: string
38
+ intervalTree: Record<string, IntervalTree>
39
+ }>
74
40
 
75
41
  public async getHeader() {
76
- const { header } = await this.decodeFileContents()
42
+ const { header } = await this.setup()
77
43
  return header
78
44
  }
79
45
 
80
46
  async getMetadata() {
81
- const { header } = await this.decodeFileContents()
47
+ const { header } = await this.setup()
82
48
  const parser = new VCF({ header: header })
83
49
  return parser.getMetadata()
84
50
  }
85
51
 
86
- public async getLines() {
87
- const { header, lines } = await this.decodeFileContents()
52
+ // converts lines into an interval tree
53
+ public async setupP() {
54
+ const buffer = await openLocation(
55
+ readConfObject(this.config, 'vcfLocation'),
56
+ this.pluginManager,
57
+ ).readFile()
58
+
59
+ const buf = isGzip(buffer) ? await unzip(buffer) : buffer
88
60
 
89
- const parser = new VCF({ header: header })
61
+ // 512MB max chrome string length is 512MB
62
+ if (buf.length > 536_870_888) {
63
+ throw new Error('Data exceeds maximum string length (512MB)')
64
+ }
90
65
 
91
- return lines.map((line, index) => {
92
- return new VcfFeature({
93
- variant: parser.parseLine(line),
94
- parser,
95
- id: `${this.id}-vcf-${index}`,
66
+ const str = new TextDecoder().decode(buf)
67
+ const { header, lines } = readVcf(str)
68
+
69
+ const intervalTree = lines
70
+ .map((line, id) => {
71
+ const [refName, startP, , ref, , , , info] = line.split('\t')
72
+ const start = +startP - 1
73
+ const end = +(info.match(/END=(\d+)/)?.[1].trim() || start + ref.length)
74
+ return { line, refName, start, end, id }
96
75
  })
97
- })
76
+ .reduce((acc, obj) => {
77
+ const key = obj.refName
78
+ if (!acc[key]) {
79
+ acc[key] = new IntervalTree()
80
+ }
81
+ acc[key].insert([obj.start, obj.end], obj)
82
+ return acc
83
+ }, {} as Record<string, IntervalTree>)
84
+
85
+ return { header, intervalTree }
98
86
  }
99
87
 
100
88
  public async setup() {
101
89
  if (!this.vcfFeatures) {
102
- this.vcfFeatures = this.getLines().then(feats => {
103
- return feats.reduce(
104
- (acc: Record<string, IntervalTree>, obj: VcfFeature) => {
105
- const key = obj.get('refName')
106
- if (!acc[key]) {
107
- acc[key] = new IntervalTree()
108
- }
109
- acc[key].insert([obj.get('start'), obj.get('end')], obj)
110
- return acc
111
- },
112
- {},
113
- )
90
+ this.vcfFeatures = this.setupP().catch(e => {
91
+ this.vcfFeatures = undefined
92
+ throw e
114
93
  })
115
94
  }
116
95
  return this.vcfFeatures
117
96
  }
118
97
 
119
98
  public async getRefNames(_: BaseOptions = {}) {
120
- const { refNames } = await this.decodeFileContents()
121
- return refNames
99
+ const { intervalTree } = await this.setup()
100
+ return Object.keys(intervalTree)
122
101
  }
123
102
 
124
103
  public getFeatures(region: Region, opts: BaseOptions = {}) {
125
104
  return ObservableCreate<Feature>(async observer => {
126
105
  try {
127
106
  const { start, end, refName } = region
128
- const vcfFeatures = await this.setup()
129
- const tree = vcfFeatures[refName]
130
- const feats = tree.search([start, end]) // expected array ['val1']
131
- feats.forEach(f => {
132
- observer.next(f)
133
- })
107
+ const { header, intervalTree } = await this.setup()
108
+ const parser = new VCF({ header: header })
109
+ intervalTree[refName]?.search([start, end]).forEach(f =>
110
+ observer.next(
111
+ new VcfFeature({
112
+ variant: parser.parseLine(f.line),
113
+ parser,
114
+ id: `${this.id}-${f.id}`,
115
+ }),
116
+ ),
117
+ )
134
118
  observer.complete()
135
119
  } catch (e) {
136
120
  observer.error(e)
@@ -66,7 +66,7 @@ Array [
66
66
  },
67
67
  "start": 276,
68
68
  "type": "SNV",
69
- "uniqueId": "test-vcf-0",
69
+ "uniqueId": "test-0",
70
70
  },
71
71
  Object {
72
72
  "ALT": Array [
@@ -132,7 +132,7 @@ Array [
132
132
  },
133
133
  "start": 1693,
134
134
  "type": "SNV",
135
- "uniqueId": "test-vcf-1",
135
+ "uniqueId": "test-1",
136
136
  },
137
137
  Object {
138
138
  "ALT": Array [
@@ -198,7 +198,7 @@ Array [
198
198
  },
199
199
  "start": 2643,
200
200
  "type": "SNV",
201
- "uniqueId": "test-vcf-2",
201
+ "uniqueId": "test-2",
202
202
  },
203
203
  Object {
204
204
  "ALT": Array [
@@ -258,7 +258,7 @@ Array [
258
258
  },
259
259
  "start": 3212,
260
260
  "type": "SNV",
261
- "uniqueId": "test-vcf-3",
261
+ "uniqueId": "test-3",
262
262
  },
263
263
  Object {
264
264
  "ALT": Array [
@@ -319,7 +319,7 @@ Array [
319
319
  },
320
320
  "start": 3857,
321
321
  "type": "deletion",
322
- "uniqueId": "test-vcf-4",
322
+ "uniqueId": "test-4",
323
323
  },
324
324
  ]
325
325
  `;
@@ -20,7 +20,7 @@ import VcfFeature from './VcfFeature'
20
20
  export default class extends BaseFeatureDataAdapter {
21
21
  protected configured?: Promise<{
22
22
  vcf: TabixIndexedFile
23
- parser: typeof VcfParser
23
+ parser: VcfParser
24
24
  filehandle: GenericFilehandle
25
25
  }>
26
26
 
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  createBaseTrackConfig,
7
7
  createBaseTrackModel,
8
8
  } from '@jbrowse/core/pluggableElementTypes/models'
9
+ import { FileLocation } from '@jbrowse/core/util/types'
9
10
  import TrackType from '@jbrowse/core/pluggableElementTypes/TrackType'
10
11
  import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType'
11
12
  import Plugin from '@jbrowse/core/Plugin'
@@ -23,6 +24,13 @@ import {
23
24
  } from './VariantFeatureWidget'
24
25
  import { configSchema as vcfTabixAdapterConfigSchema } from './VcfTabixAdapter'
25
26
  import { configSchema as vcfAdapterConfigSchema } from './VcfAdapter'
27
+ import {
28
+ makeIndex,
29
+ makeIndexType,
30
+ AdapterGuesser,
31
+ getFileName,
32
+ TrackTypeGuesser,
33
+ } from '@jbrowse/core/util/tracks'
26
34
 
27
35
  export default class VariantsPlugin extends Plugin {
28
36
  name = 'VariantsPlugin'
@@ -37,6 +45,43 @@ export default class VariantsPlugin extends Plugin {
37
45
  import('./VcfTabixAdapter/VcfTabixAdapter').then(r => r.default),
38
46
  }),
39
47
  )
48
+ pluginManager.addToExtensionPoint(
49
+ 'Core-guessAdapterForLocation',
50
+ (adapterGuesser: AdapterGuesser) => {
51
+ return (
52
+ file: FileLocation,
53
+ index?: FileLocation,
54
+ adapterHint?: string,
55
+ ) => {
56
+ const regexGuess = /\.vcf\.b?gz$/i
57
+ const adapterName = 'VcfTabixAdapter'
58
+ const fileName = getFileName(file)
59
+ const indexName = index && getFileName(index)
60
+ if (regexGuess.test(fileName) || adapterHint === adapterName) {
61
+ return {
62
+ type: adapterName,
63
+ vcfGzLocation: file,
64
+ index: {
65
+ location: index || makeIndex(file, '.tbi'),
66
+ indexType: makeIndexType(indexName, 'CSI', 'TBI'),
67
+ },
68
+ }
69
+ }
70
+ return adapterGuesser(file, index, adapterHint)
71
+ }
72
+ },
73
+ )
74
+ pluginManager.addToExtensionPoint(
75
+ 'Core-guessTrackTypeForLocation',
76
+ (trackTypeGuesser: TrackTypeGuesser) => {
77
+ return (adapterName: string) => {
78
+ if (adapterName === 'VcfTabixAdapter') {
79
+ return 'VariantTrack'
80
+ }
81
+ return trackTypeGuesser(adapterName)
82
+ }
83
+ },
84
+ )
40
85
 
41
86
  pluginManager.addAdapterType(
42
87
  () =>
@@ -48,6 +93,28 @@ export default class VariantsPlugin extends Plugin {
48
93
  }),
49
94
  )
50
95
 
96
+ pluginManager.addToExtensionPoint(
97
+ 'Core-guessAdapterForLocation',
98
+ (adapterGuesser: AdapterGuesser) => {
99
+ return (
100
+ file: FileLocation,
101
+ index?: FileLocation,
102
+ adapterHint?: string,
103
+ ) => {
104
+ const regexGuess = /\.vcf$/i
105
+ const adapterName = 'VcfAdapter'
106
+ const fileName = getFileName(file)
107
+ if (regexGuess.test(fileName) || adapterHint === adapterName) {
108
+ return {
109
+ type: adapterName,
110
+ vcfLocation: file,
111
+ }
112
+ }
113
+ return adapterGuesser(file, index, adapterHint)
114
+ }
115
+ },
116
+ )
117
+
51
118
  pluginManager.addRendererType(() =>
52
119
  pluginManager.jbrequire(StructuralVariantChordRendererFactory),
53
120
  )
package/src/declare.d.ts DELETED
@@ -1,2 +0,0 @@
1
- declare module '@gmod/vcf'
2
- declare module '@gmod/bgzf-filehandle'