Skip to content

Instantly share code, notes, and snippets.

@m1k1o
Last active September 21, 2019 21:05
Show Gist options
  • Save m1k1o/fe51ce4862853f13620a4312a693eeef to your computer and use it in GitHub Desktop.
Save m1k1o/fe51ce4862853f13620a4312a693eeef to your computer and use it in GitHub Desktop.
Simple vCard JavaScript parser.
/* Usage:
* require() as module, use as parseVCF(contents, debug = false): JSON
* node index.js json < input.vcf > output.json
* node index.js < input.vcf > output.csv
*/
function parseVCF(contents, debug = false) {
// Unfold
contents = contents.replace(/([\n\r]{1,2}) /g, '')
contents = contents.replace(/=([\n\r]{1,2})=/g, '=')
contents = contents.replace(/=([\n\r]{1,2})([\n\r]{1,2})/g, '$1')
// Match contacts
let contacts = contents.match(/BEGIN:VCARD(.|\s)*?END:VCARD/g);
debug && console.error("Found contacts: " + contacts.length);
return contacts.map(str => str
.replace(/\s*(BEGIN|END):VCARD\s*/g, '')
.split(/\s*[\n\r]{1,2}\s*/)
.map(prop => {
let resp = prop.match(/^([^:]+):(.*)/);
if(resp == null) {
debug && console.error("Something went wrong...");
return {};
}
// Extract meta
let meta = {};
resp[1].split(";")
.map(function (p, i) {
let match = p.match(/([a-z]+)=(.*)/i);
if (match) {
return [match[1], match[2]];
} else {
return ["TYPE" + (i === 0 ? "" : i), p];
}
})
.forEach(function (p) {
meta[p[0]] = p[1];
});
let value = resp[2];
// QUOTED-PRINTABLE decoding.
if(meta.ENCODING == "QUOTED-PRINTABLE" && meta.CHARSET == "UTF-8"){
delete meta.ENCODING;
delete meta.CHARSET;
value = value.replace(/([=a-fA-F0-9]+)/g, function(_, chain) {
if(!/^(=[a-fA-F0-9]{2})+$/.test(chain)) {
return _;
}
let chars = chain
.split("=")
.map(hex =>
parseInt(hex,16))
.filter(Number.isInteger);
let buf = Buffer.alloc(chars.length);
let x = 0;
for(let i in chars) {
buf.writeUInt8(chars[i], x++);
}
return buf.toString();
})
}
value = value.split(";");
return { meta, value }
}));
}
if (require.main === module) {
const fs = require('fs');
let contents = fs.readFileSync(0, 'utf-8');
let data = parseVCF(contents, true);
if(process.argv[2] == "json") {
console.log(JSON.stringify(data, null, 2));
} else {
let csv = contacts.map(contact => {
let RES = {
FN: [], // Full Name
ORG: [], // Organisation
N: [], // Names
TEL: [] // Tels
};
contact.forEach(({ meta, value }) => {
if(meta.TYPE == "N" || meta.TYPE == "FN" || meta.TYPE == "ORG") {
RES[meta.TYPE] = value;
}
if(meta.TYPE == "TEL") {
RES[meta.TYPE].push(value)
}
})
return Object.values(RES).map(x => x.join("\t")).join("\t");
// Full Name
// Organisation
// Family Name
// Given Name
// Additional Names
// Honorific Prefixes
// Honorific Suffixes
// Tel1
// Tel2
// Tel3
// ...
}).join("\n");
console.log(csv);
}
} else {
module.exports = parseVCF;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment