UNPKG

@gmod/vcf

Version:

High performance streaming Variant Call Format (VCF) parser in pure JavaScript

104 lines 3.65 kB
import { Variant } from "./Variant.js"; import { parseStructuredMetaVal } from "./parseMetaString.js"; import vcfReserved from "./vcfReserved.js"; export { Variant } from "./Variant.js"; export default class VCFParser { metadata; strict; samples; constructor({ header, strict = true }) { if (!header.length) { throw new Error('empty header received'); } const headerLines = header.split(/[\r\n]+/).filter(Boolean); if (!headerLines.length) { throw new Error('no non-empty header lines specified'); } this.strict = strict; this.metadata = { INFO: { ...vcfReserved.InfoFields }, FORMAT: { ...vcfReserved.GenotypeFields }, ALT: { ...vcfReserved.AltTypes }, FILTER: { ...vcfReserved.FilterTypes }, }; let lastLine; for (let i = 0; i < headerLines.length; i++) { const line = headerLines[i] ?? ''; if (!line.startsWith('#')) { throw new Error(`Bad line in header:\n${line}`); } else if (line.startsWith('##')) { this.parseMetadata(line); } else { lastLine = line; } } if (!lastLine) { throw new Error('No format line found in header'); } const fields = lastLine.trim().split('\t'); const thisHeader = fields.slice(0, 8); const correctHeader = [ '#CHROM', 'POS', 'ID', 'REF', 'ALT', 'QUAL', 'FILTER', 'INFO', ]; if (fields.length < 8) { throw new Error(`VCF header missing columns:\n${lastLine}`); } else if (thisHeader.length !== correctHeader.length || !thisHeader.every((value, index) => value === correctHeader[index])) { throw new Error(`VCF column headers not correct:\n${lastLine}`); } this.samples = fields.slice(9); } parseMetadata(line) { const match = /^##(.+?)=(.*)/.exec(line.trim()); if (!match) { throw new Error(`Line is not a valid metadata line: ${line}`); } const r = match[1] ?? ''; const metaVal = match[2]; if (metaVal?.startsWith('<')) { const existing = this.metadata[r]; const section = existing && typeof existing === 'object' ? existing : {}; const [id, keyVals] = parseStructuredMetaVal(metaVal); if (typeof id === 'string') { // ##INFO=<ID=AF_ESP,...> section[id] = keyVals; this.metadata[r] = section; } else { // ##ID=<Description="ClinVar Variation ID"> this.metadata[r] = keyVals; } } else { this.metadata[r] = metaVal; } } getMetadata(...args) { let filteredMetadata = this.metadata; for (const arg of args) { if (typeof filteredMetadata !== 'object' || filteredMetadata === null) { return undefined; } filteredMetadata = filteredMetadata[arg]; if (filteredMetadata === undefined) { return undefined; } } return filteredMetadata; } // SAMPLES() and GENOTYPES() on the returned Variant are lazily evaluated. parseLine(line) { return new Variant(line, this.metadata.INFO, this.metadata.FORMAT, this.samples, this.strict); } } //# sourceMappingURL=parse.js.map