UNPKG

@gmod/vcf

Version:

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

112 lines 4.15 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Variant = void 0; const Variant_ts_1 = require("./Variant.js"); const parseMetaString_ts_1 = require("./parseMetaString.js"); const vcfReserved_ts_1 = __importDefault(require("./vcfReserved.js")); var Variant_ts_2 = require("./Variant.js"); Object.defineProperty(exports, "Variant", { enumerable: true, get: function () { return Variant_ts_2.Variant; } }); 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_ts_1.default.InfoFields }, FORMAT: { ...vcfReserved_ts_1.default.GenotypeFields }, ALT: { ...vcfReserved_ts_1.default.AltTypes }, FILTER: { ...vcfReserved_ts_1.default.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] = (0, parseMetaString_ts_1.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_ts_1.Variant(line, this.metadata.INFO, this.metadata.FORMAT, this.samples, this.strict); } } exports.default = VCFParser; //# sourceMappingURL=parse.js.map