msa-parsers 7.0.0 → 8.1.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 CHANGED
@@ -30,9 +30,9 @@ pnpm add msa-parsers
30
30
 
31
31
  ### Parsing MSA files
32
32
 
33
- `parseMSA` sniffs the format from the content, not from the file name. Pass a
34
- format as the third argument to settle it yourself -- FASTA and A3M share a
35
- leading `>`, so auto-detection between them is a heuristic:
33
+ `parseMSA` sniffs the format from the content, not from the file name. FASTA and
34
+ A3M share a leading `>`, so telling them apart is heuristic; pass a format as
35
+ the third argument to override the guess:
36
36
 
37
37
  ```typescript
38
38
  import { parseMSA } from 'msa-parsers'
@@ -77,7 +77,7 @@ const treeWithIds = generateNodeIds(tree)
77
77
 
78
78
  Every source of overlay annotations converts to one flat list of `Annotation` (a
79
79
  row name, an accession, a name, a description and a 1-based inclusive interval),
80
- and that list is what the viewer draws.
80
+ and the viewer draws that list.
81
81
 
82
82
  ```typescript
83
83
  import {
@@ -22,6 +22,37 @@ function geneStrand({ type, strand }) {
22
22
  const directional = strand === '+' ? 1 : strand === '-' ? -1 : undefined;
23
23
  return GENE_LEVEL_TYPES.has(type) ? directional : undefined;
24
24
  }
25
+ // The nine columns a feature line holds; every other key of a record came from
26
+ // column 9
27
+ const GFF_COLUMNS = new Set([
28
+ 'seq_id',
29
+ 'source',
30
+ 'type',
31
+ 'start',
32
+ 'end',
33
+ 'score',
34
+ 'strand',
35
+ 'phase',
36
+ ]);
37
+ function attributesOf(record) {
38
+ const entries = Object.entries(record).filter(([key, value]) => !GFF_COLUMNS.has(key) && typeof value === 'string');
39
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
40
+ }
41
+ // JBrowse and IGV read an RGB triple as well as a named or hex color, and the
42
+ // attribute parser has already turned `255,0,0` into `255 0 0` by splitting on
43
+ // the GFF3 multi-value comma
44
+ const RGB_TRIPLE = /^(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})$/;
45
+ function cssColor(record) {
46
+ const value = (record.color ??
47
+ record.colour ??
48
+ record.Color ??
49
+ record.Colour);
50
+ if (!value) {
51
+ return undefined;
52
+ }
53
+ const triple = RGB_TRIPLE.exec(value.trim());
54
+ return triple ? `rgb(${triple[1]},${triple[2]},${triple[3]})` : value.trim();
55
+ }
25
56
  // InterProScan describes the sequence it scanned before listing the matches
26
57
  // against it. Those lines span the whole row and carry no signature, so they
27
58
  // would draw a full-width block labelled by an md5.
@@ -58,6 +89,8 @@ export function gffToAnnotations(gffRecords) {
58
89
  start: record.start,
59
90
  end: record.end,
60
91
  strand: geneStrand(record),
92
+ color: cssColor(record),
93
+ attributes: attributesOf(record),
61
94
  };
62
95
  });
63
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gffToAnnotations.js","sourceRoot":"","sources":["../../src/gff/gffToAnnotations.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,MAAM;IACN,YAAY;IACZ,MAAM;IACN,YAAY;IACZ,oBAAoB;IACpB,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;IACP,SAAS;CACV,CAAC,CAAA;AAEF,0EAA0E;AAC1E,oCAAoC;AACpC,SAAS,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAa;IAC7C,MAAM,WAAW,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACxE,OAAO,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;AAC7D,CAAC;AAED,4EAA4E;AAC5E,6EAA6E;AAC7E,oDAAoD;AACpD,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAA;AAErE;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAuB;IACtD,OAAO,UAAU;SACd,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SACxD,GAAG,CAAC,MAAM,CAAC,EAAE;QACZ,MAAM,SAAS,GACZ,MAAM,CAAC,IAAe;YACtB,MAAM,CAAC,EAAa;YACrB,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG,EAAE,CAAA;QAClD,MAAM,IAAI,GACP,MAAM,CAAC,cAAyB;YAChC,MAAM,CAAC,IAAe;YACvB,SAAS,CAAA;QACX,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,MAAM;YACjB,SAAS;YACT,IAAI;YACJ,kEAAkE;YAClE,4DAA4D;YAC5D,8CAA8C;YAC9C,WAAW,EACR,MAAM,CAAC,WAAsB;gBAC7B,MAAM,CAAC,IAAe;gBACtB,MAAM,CAAC,cAAyB;gBACjC,IAAI;YACN,WAAW,EAAE,MAAM,CAAC,IAAI;YACxB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;SAC3B,CAAA;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
1
+ {"version":3,"file":"gffToAnnotations.js","sourceRoot":"","sources":["../../src/gff/gffToAnnotations.ts"],"names":[],"mappings":"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"}
@@ -5,13 +5,9 @@ export default abstract class BaseMSA {
5
5
  abstract getNames(): string[];
6
6
  private width?;
7
7
  /**
8
- * Column count of the alignment: the length of its widest row.
9
- *
10
- * Not the first row's length. Ragged input is real -- a hand-edited fasta, or
11
- * an aligner that stops a row at its last residue -- and taking row 0 makes
12
- * every column past it unreachable when row 0 is the short one. The viewer's
13
- * gap analysis already defines the column count this way, so anything else
14
- * disagrees with it.
8
+ * Column count of the alignment: the length of its widest row, since a
9
+ * hand-edited fasta or some aligners leave rows short. The viewer's gap
10
+ * analysis counts columns the same way.
15
11
  */
16
12
  getWidth(): number;
17
13
  getTree(): NodeWithIds;
@@ -1,13 +1,9 @@
1
1
  export default class BaseMSA {
2
2
  width;
3
3
  /**
4
- * Column count of the alignment: the length of its widest row.
5
- *
6
- * Not the first row's length. Ragged input is real -- a hand-edited fasta, or
7
- * an aligner that stops a row at its last residue -- and taking row 0 makes
8
- * every column past it unreachable when row 0 is the short one. The viewer's
9
- * gap analysis already defines the column count this way, so anything else
10
- * disagrees with it.
4
+ * Column count of the alignment: the length of its widest row, since a
5
+ * hand-edited fasta or some aligners leave rows short. The viewer's gap
6
+ * analysis counts columns the same way.
11
7
  */
12
8
  getWidth() {
13
9
  this.width ??= this.getNames().reduce((max, name) => Math.max(max, this.getRow(name).length), 0);
@@ -1 +1 @@
1
- {"version":3,"file":"BaseMSA.js","sourceRoot":"","sources":["../../src/msa/BaseMSA.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAgB,OAAO;IAK3B,KAAK,CAAS;IAEtB;;;;;;;;OAQG;IACH,QAAQ;QACN,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CACnC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EACtD,CAAC,CACF,CAAA;QACD,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,OAAO;QACL,OAAO;YACL,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrC,EAAE,EAAE,IAAI;gBACR,QAAQ,EAAE,EAAE;gBACZ,IAAI;aACL,CAAC,CAAC;SACJ,CAAA;IACH,CAAC;IAED,UAAU,CACR,KAAa;QAEb,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,aAAa;QACX,OAAO,EAAE,CAAA;IACX,CAAC;IAED,SAAS;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,YAAY;QACd,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,2BAA2B;QAC7B,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,EAAE,CAAA;IACX,CAAC;CACF"}
1
+ {"version":3,"file":"BaseMSA.js","sourceRoot":"","sources":["../../src/msa/BaseMSA.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAgB,OAAO;IAK3B,KAAK,CAAS;IAEtB;;;;OAIG;IACH,QAAQ;QACN,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CACnC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EACtD,CAAC,CACF,CAAA;QACD,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,OAAO;QACL,OAAO;YACL,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrC,EAAE,EAAE,IAAI;gBACR,QAAQ,EAAE,EAAE;gBACZ,IAAI;aACL,CAAC,CAAC;SACJ,CAAA;IACH,CAAC;IAED,UAAU,CACR,KAAa;QAEb,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,aAAa;QACX,OAAO,EAAE,CAAA;IACX,CAAC;IAED,SAAS;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,IAAI,YAAY;QACd,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,2BAA2B;QAC7B,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,EAAE,CAAA;IACX,CAAC;CACF"}
@@ -49,6 +49,9 @@ export function parseAll(text, opts) {
49
49
  for (const line of lines) {
50
50
  let match;
51
51
  if (formatStartRegex.test(line)) {
52
+ if (stock) {
53
+ db.push(stock);
54
+ }
52
55
  stock = createStockholm();
53
56
  treeContinues = false;
54
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stockholmParser.js","sourceRoot":"","sources":["../../src/msa/stockholmParser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,SAAS,eAAe;IACtB,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACvB,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACvB,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACvB,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACvB,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;KAC7B,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,kDAAkD;AAClD,SAAS,WAAW,CAClB,KAA2B,EAC3B,MAAe;IAEf,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAA;IACd,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,eAAe,EAAE,CAAA;AAC1B,CAAC;AAED,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAC3C,MAAM,cAAc,GAAG,WAAW,CAAA;AAClC,MAAM,OAAO,GAAG,2BAA2B,CAAA;AAC3C,MAAM,OAAO,GAAG,2BAA2B,CAAA;AAC3C,MAAM,OAAO,GAAG,mCAAmC,CAAA;AACnD,MAAM,OAAO,GAAG,mCAAmC,CAAA;AACnD,8EAA8E;AAC9E,yEAAyE;AACzE,6EAA6E;AAC7E,MAAM,SAAS,GAAG,6BAA6B,CAAA;AAC/C,MAAM,aAAa,GAAG,IAAI,CAAA;AAE1B,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,IAA2B;IAE3B,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAA;IAC1B,MAAM,EAAE,GAAoB,EAAE,CAAA;IAC9B,IAAI,KAAK,GAAyB,IAAI,CAAA;IACtC,wEAAwE;IACxE,6CAA6C;IAC7C,IAAI,aAAa,GAAG,KAAK,CAAA;IAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,KAA6B,CAAA;QAEjC,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,KAAK,GAAG,eAAe,EAAE,CAAA;YACzB,aAAa,GAAG,KAAK,CAAA;QACvB,CAAC;aAAM,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,KAAK,EAAE,CAAC;gBACV,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;YACD,KAAK,GAAG,IAAI,CAAA;QACd,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxC,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;YACrB,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YACrC,IAAI,GAAG,KAAK,IAAI,IAAI,aAAa,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvD,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAA;YACxC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;YACxB,CAAC;YACD,aAAa,GAAG,GAAG,KAAK,IAAI,CAAA;QAC9B,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxC,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5C,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QAC/D,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxC,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5C,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAC3D;YAAA,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;QAC7C,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxC,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5C,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YAC5D,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QAC3D,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC1C,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;YACzB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;YACzB,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;gBACzC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAA;gBAC3B,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAC7B,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAA;QACnC,CAAC;aAAM,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAChB,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,KAAK,CACnB,IAAY,EACZ,IAA2B;IAE3B,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAClD,CAAC;IACD,OAAO,EAAE,CAAC,CAAC,CAAE,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"stockholmParser.js","sourceRoot":"","sources":["../../src/msa/stockholmParser.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -58,6 +58,13 @@ export interface Annotation {
58
58
  start: number;
59
59
  end: number;
60
60
  strand?: number;
61
+ /**
62
+ * CSS color the feature carries itself, from a GFF3 `color=` attribute. It
63
+ * overrides the palette and any scale a channel resolves.
64
+ */
65
+ color?: string;
66
+ /** the GFF attributes of column 9, for a channel encoding one of them */
67
+ attributes?: Record<string, string>;
61
68
  }
62
69
  /**
63
70
  * One signature hit. `entry` is the InterPro entry the signature is integrated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msa-parsers",
3
- "version": "7.0.0",
3
+ "version": "8.1.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@gmod/newick": "^1.0.3",
24
- "clustal-js": "^2.0.17",
24
+ "clustal-js": "^2.0.18",
25
25
  "emf-js": "^2.0.0"
26
26
  },
27
27
  "publishConfig": {
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "typescript": "^7.0.2",
32
- "vitest": "^4.1.10"
32
+ "vitest": "^5.0.0"
33
33
  },
34
34
  "scripts": {
35
35
  "clean": "rimraf dist",
@@ -41,6 +41,12 @@ test('maps a record onto the annotation fields', () => {
41
41
  start: 10,
42
42
  end: 50,
43
43
  strand: undefined,
44
+ color: undefined,
45
+ attributes: {
46
+ Name: 'PF00001',
47
+ signature_desc: '7tm_1',
48
+ description: 'GPCR family',
49
+ },
44
50
  },
45
51
  ])
46
52
  })
@@ -104,3 +110,31 @@ test('falls back from Name to ID to source and positions', () => {
104
110
  ]).map(a => a.accession),
105
111
  ).toEqual(['domain_123', 'CustomSource_10_50'])
106
112
  })
113
+
114
+ test('keeps the column 9 attributes, so a channel can encode one of them', () => {
115
+ expect(
116
+ gffToAnnotations([record({ type: 'gene', Name: 'trpB', gene: 'trpB' })])[0]
117
+ ?.attributes,
118
+ ).toEqual({ Name: 'trpB', gene: 'trpB' })
119
+ })
120
+
121
+ test('reads a color attribute under any of its spellings', () => {
122
+ expect(
123
+ gffToAnnotations([
124
+ record({ color: '#ff0000' }),
125
+ record({ colour: 'rebeccapurple' }),
126
+ record({ Color: '#00ff00' }),
127
+ record({ Colour: 'blue' }),
128
+ record({}),
129
+ ]).map(a => a.color),
130
+ ).toEqual(['#ff0000', 'rebeccapurple', '#00ff00', 'blue', undefined])
131
+ })
132
+
133
+ test('normalizes an RGB triple, which the attribute parser hands over space-separated', () => {
134
+ expect(
135
+ gffToAnnotations([
136
+ record({ color: '255 0 0' }),
137
+ record({ color: '0,128,255' }),
138
+ ]).map(a => a.color),
139
+ ).toEqual(['rgb(255,0,0)', 'rgb(0,128,255)'])
140
+ })
@@ -26,6 +26,43 @@ function geneStrand({ type, strand }: GFFRecord): number | undefined {
26
26
  return GENE_LEVEL_TYPES.has(type) ? directional : undefined
27
27
  }
28
28
 
29
+ // The nine columns a feature line holds; every other key of a record came from
30
+ // column 9
31
+ const GFF_COLUMNS = new Set([
32
+ 'seq_id',
33
+ 'source',
34
+ 'type',
35
+ 'start',
36
+ 'end',
37
+ 'score',
38
+ 'strand',
39
+ 'phase',
40
+ ])
41
+
42
+ function attributesOf(record: GFFRecord) {
43
+ const entries = Object.entries(record).filter(
44
+ ([key, value]) => !GFF_COLUMNS.has(key) && typeof value === 'string',
45
+ ) as [string, string][]
46
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined
47
+ }
48
+
49
+ // JBrowse and IGV read an RGB triple as well as a named or hex color, and the
50
+ // attribute parser has already turned `255,0,0` into `255 0 0` by splitting on
51
+ // the GFF3 multi-value comma
52
+ const RGB_TRIPLE = /^(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})$/
53
+
54
+ function cssColor(record: GFFRecord) {
55
+ const value = (record.color ??
56
+ record.colour ??
57
+ record.Color ??
58
+ record.Colour) as string | undefined
59
+ if (!value) {
60
+ return undefined
61
+ }
62
+ const triple = RGB_TRIPLE.exec(value.trim())
63
+ return triple ? `rgb(${triple[1]},${triple[2]},${triple[3]})` : value.trim()
64
+ }
65
+
29
66
  // InterProScan describes the sequence it scanned before listing the matches
30
67
  // against it. Those lines span the whole row and carry no signature, so they
31
68
  // would draw a full-width block labelled by an md5.
@@ -66,6 +103,8 @@ export function gffToAnnotations(gffRecords: GFFRecord[]): Annotation[] {
66
103
  start: record.start,
67
104
  end: record.end,
68
105
  strand: geneStrand(record),
106
+ color: cssColor(record),
107
+ attributes: attributesOf(record),
69
108
  }
70
109
  })
71
110
  }
@@ -8,13 +8,9 @@ export default abstract class BaseMSA {
8
8
  private width?: number
9
9
 
10
10
  /**
11
- * Column count of the alignment: the length of its widest row.
12
- *
13
- * Not the first row's length. Ragged input is real -- a hand-edited fasta, or
14
- * an aligner that stops a row at its last residue -- and taking row 0 makes
15
- * every column past it unreachable when row 0 is the short one. The viewer's
16
- * gap analysis already defines the column count this way, so anything else
17
- * disagrees with it.
11
+ * Column count of the alignment: the length of its widest row, since a
12
+ * hand-edited fasta or some aligners leave rows short. The viewer's gap
13
+ * analysis counts columns the same way.
18
14
  */
19
15
  getWidth(): number {
20
16
  this.width ??= this.getNames().reduce(
@@ -133,6 +133,17 @@ seq2 GHIKL
133
133
  })
134
134
  })
135
135
 
136
+ test('a header ends the open alignment when its // is missing', () => {
137
+ const db = parseAll(`# STOCKHOLM 1.0
138
+ seq1 ACDEF
139
+ # STOCKHOLM 1.0
140
+ seq2 GHIKL
141
+ //`)
142
+ expect(db).toHaveLength(2)
143
+ expect(db[0]?.seqdata).toEqual({ seq1: 'ACDEF' })
144
+ expect(db[1]?.seqdata).toEqual({ seq2: 'GHIKL' })
145
+ })
146
+
136
147
  test('joins a tree split across #=GF NH lines, starting a new one at TN', () => {
137
148
  const [aln] = parseAll(`# STOCKHOLM 1.0
138
149
  #=GF TN first
@@ -71,6 +71,9 @@ export function parseAll(
71
71
  let match: RegExpExecArray | null
72
72
 
73
73
  if (formatStartRegex.test(line)) {
74
+ if (stock) {
75
+ db.push(stock)
76
+ }
74
77
  stock = createStockholm()
75
78
  treeContinues = false
76
79
  } else if (formatEndRegex.test(line)) {
package/src/types.ts CHANGED
@@ -64,6 +64,13 @@ export interface Annotation {
64
64
  start: number
65
65
  end: number
66
66
  strand?: number
67
+ /**
68
+ * CSS color the feature carries itself, from a GFF3 `color=` attribute. It
69
+ * overrides the palette and any scale a channel resolves.
70
+ */
71
+ color?: string
72
+ /** the GFF attributes of column 9, for a channel encoding one of them */
73
+ attributes?: Record<string, string>
67
74
  }
68
75
 
69
76
  /**