@gmod/vcf
Version:
High performance streaming Variant Call Format (VCF) parser in pure JavaScript
112 lines • 4.15 kB
JavaScript
;
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