parse-gedcom
Version:
a simple and readable gedcom parser
3 lines (2 loc) • 5.66 kB
JavaScript
import E from"unist-util-visit-parents";import A from"unist-util-remove";import t from"graphlib-dot";import{Graph as e}from"graphlib";const I=new RegExp("^([0-9]*)"),N=new RegExp("(\\s+)"),n=new RegExp(`^([${N}])`),R=new RegExp(`^@([A-ZÀ-ÿa-z_0-9])([A-ZÀ-ÿa-z_0-9${N}#])*@`),r=new RegExp("^(_?[A-ZÀ-ÿa-z_0-9]+)"),T=new RegExp(/^(.*)/);function o(E){function A(A,t){const e=E.match(A);if(!e)throw new Error(t);return E=E.substring(e[0].length),e[1]}let t;E=E.trimStart();const e=A(I,"Expected level");if(e.length>2||2===e.length&&"0"===e[0])throw new Error(`Invalid level: ${e}`);const N=parseInt(e);A(n,"Expected delimiter after level");const o=E.match(R);o&&(t=o[0],E=E.substring(o[0].length),A(n,"Expected delimiter after pointer"));let a={level:N,tag:A(r,"Expected tag")};t&&(a.xref_id=t);const C=E.match(n);if(C){const A=(E=E.substring(C[0].length)).match(R),t=E.match(T);A?a.pointer=A[0]:t&&(a.value=t[1])}return a}const a={ABBR:"ABBREVIATION",ADDR:"ADDRESS",ADR1:"ADDRESS1",ADR2:"ADDRESS2",ADOP:"ADOPTION",AFN:"AFN",AGE:"AGE",AGNC:"AGENCY",ALIA:"ALIAS",ANCE:"ANCESTORS",ANCI:"ANCES_INTEREST",ANUL:"ANNULMENT",ASSO:"ASSOCIATES",AUTH:"AUTHOR",BAPL:"BAPTISM-LDS",BAPM:"BAPTISM",BARM:"BAR_MITZVAH",BASM:"BAS_MITZVAH",BIRT:"BIRTH",BLES:"BLESSING",BURI:"BURIAL",CALN:"CALL_NUMBER",CAST:"CASTE",CAUS:"CAUSE",CENS:"CENSUS",CHAN:"CHANGE",CHAR:"CHARACTER",CHIL:"CHILD",CHR:"CHRISTENING",CHRA:"ADULT_CHRISTENING",CITY:"CITY",CONC:"CONCATENATION",CONF:"CONFIRMATION",CONL:"CONFIRMATION_LDS",CONT:"CONTINUED",COPR:"COPYRIGHT",CORP:"CORPORATE",CREM:"CREMATION",CTRY:"COUNTRY",DATA:"DATA",DATE:"DATE",DEAT:"DEATH",DESC:"DESCENDANTS",DESI:"DESCENDANT_INT",DEST:"DESTINATION",DIV:"DIVORCE",DIVF:"DIVORCE_FILED",DSCR:"PHY_DESCRIPTION",EDUC:"EDUCATION",EMAI:"EMAIL",EMIG:"EMIGRATION",ENDL:"ENDOWMENT",ENGA:"ENGAGEMENT",EVEN:"EVENT",FACT:"FACT",FAM:"FAMILY",FAMC:"FAMILY_CHILD",FAMF:"FAMILY_FILE",FAMS:"FAMILY_SPOUSE",FAX:"FACIMILIE",FCOM:"FIRST_COMMUNICATION",FILE:"FILE",FORM:"FORMAT",FONE:"PHONETIC",GEDC:"GEDCOM",GIVN:"GIVEN_NAME",GRAD:"GRADUATION",HEAD:"HEADER",HUSB:"HUSBAND",IDNO:"IDENT_NUMVER",IMMI:"IMMIGRATION",INDI:"INDIVIDUAL",LANG:"LANGUAGE",LATI:"LATITUDE",LONG:"LONGITUDE",MAP:"MAP",MARB:"MARRIAGE_BANN",MARC:"MARRIAGE_CONTRACT",MARL:"MARRIAGE_LICENSE",MARR:"MARRIAGE",MARS:"MARRIAGE_SETTLEMENT",MEDI:"MEDIA",NAME:"NAME",NATI:"NATIONALITY",NATU:"NATURALIZATION",NCHI:"CHILDREN_COUNT",NICK:"NICKNAME",NMR:"MARRIAGE_COUNT",NOTE:"NOTE",NPFX:"NAME_PREFIX",NSFX:"NAME_SUFFIX",OBJE:"OBJECT",OCCU:"OCCUPATION",ORDI:"ORDINANCE",ORDN:"ORDINATION",PAGE:"PAGE",PEDI:"PEDIGREE",PHON:"PHONE",PLAC:"PLACE",POST:"POSTAL_CODE",PROB:"PROBATE",PROP:"PROPERTY",PUBL:"PUBLICATION",QUAY:"QUALITY_OF_DATA",REFN:"REFERENCE",RELA:"RELATIONSHIP",RELI:"RELIGION",REPO:"REPOSITORY",RESI:"RESIDENCE",RESN:"RESTRICTION",RETI:"RETIREMENT",RFN:"REC_FILE_NUMBER",RIN:"REC_ID_NUMBER",ROLE:"ROLE",ROMN:"ROMANIZED",SEX:"SEX",SLGC:"SEALING_CHILD",SLGS:"SEALING_SPOUCE",SOUR:"SOURCE",SPFX:"SURN_PREFIX",SSN:"SURN_PREFIX",STAE:"STATE",STAT:"STATUS",SUBM:"SUBMITTER",SUBN:"SUBMISSION",SURN:"SURNAME",TEMP:"TEMPLE",TEXT:"TEXT",TIME:"TIME",TITL:"TITLE",TRLR:"TRAILER",TYPE:"TYPE",VERS:"VERSION",WIFE:"WIFE",WILL:"WILL",WWW:"WEB"},C=new RegExp("(\\r|\\n|\\r\\n|\\n\\r)","g");function S({tag:E,value:A,xref_id:t,pointer:e}){const I={type:E,data:{formal_name:a[E]},value:A,children:[]};return t&&(I.data.xref_id=t),e&&(I.data.pointer=e),E.startsWith("_")&&(I.data.custom_tag=!0),I}function O({tag:E,value:A,pointer:t},e){if("CONC"!==E&&"CONT"!==E)return!1;if(t)throw new Error("Cannot concatenate a pointer");return e.value||(e.value=""),"CONT"===E&&(e.value+="\n"),A&&(e.value+=A),!0}function i(E){let A={type:"root",children:[]};const t=E.split(C).filter(E=>E.trim());let e=[],I=0;for(const E of t){const t=o(E);if(O(t,e[e.length-1]))continue;const N=S(t),{level:n}=t;if(0==n)A.children.push(N),e=[N];else{if(!(I==n-1||n<=I))throw new Error(`Illegal nesting: transition from ${I} to ${n}`);for(let E=0;E<=I-n;E++)e.pop();e[e.length-1].children.push(N),e.push(N)}I=n}return A}function l(E,A,t){E[A]?E["+"+A]=(E["+"+A]||[]).concat(t):E[A]=t}function M(t,e=["TRLR","SUBM","SUBN","HEAD","NOTE","SOUR"]){A(t,e);for(let A of t.children)A.data||(A.data={}),E(A,(E,t)=>{var e;const I=t.slice(1).concat(E).map(E=>{var A;return(null==(A=E.data)?void 0:A.formal_name)||E.type}).join("/");E.value?l(A.data,I,E.value):null!=(e=E.data)&&e.pointer&&l(A.data,`@${I}`,E.data.pointer)}),A.children=[];return t}function D(E){const A=M(E).children,t=new Set(A.map(E=>{var A;return null==(A=E.data)?void 0:A.xref_id}).filter(Boolean)),e=[],I=new Map;return A.forEach(E=>{E.data&&Object.entries(E.data).filter(([E,A])=>E.startsWith("@")).forEach(([A,N])=>{var n,R;if(!t.has(N))throw new Error(`Undefined reference: ${N}`);if(null==(n=E.data)||!n.xref_id)throw new Error("Link from node with no xref id");const r=null==(R=E.data)?void 0:R.xref_id,T=N,o={source:r,target:T,value:A};e.push(o);const a=[r,T].sort().join("/");I.has(a)?I.get(a).push(o):I.set(a,[o])})}),function(E,A){const t=[["@HUSBAND","@FAMILY_SPOUSE"],["@WIFE","@FAMILY_SPOUSE"],["@FAMILY_CHILD","@CHILD"]];for(let[e,I]of E)t.forEach(E=>{const[t,e]=E.map(E=>I.find(A=>A.value==E));t&&e&&A.splice(A.indexOf(t),1)})}(I,e),{nodes:A,links:e}}function L(E){const{nodes:A,links:t}=D(E);var I=new e;for(let E of A){var N;const{NAME:A}=E.data||{};I.setNode(null==(N=E.data)?void 0:N.xref_id,{label:A?A.replace(/^@/,""):E.type})}for(let E of t)I.setEdge(E.source,E.target,{label:E.value});return I}function c(E){return t.write(L(E))}export{M as compact,i as parse,D as toD3Force,c as toDot,L as toGraphlib,o as tokenize};
//# sourceMappingURL=index.modern.js.map