@treeviz/gedcom-parser 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -0
- package/bin/gedcom-parser.js +3 -0
- package/dist/classes/common.js +7 -7
- package/dist/classes/date.js +4 -4
- package/dist/classes/fam.js +3 -3
- package/dist/classes/fams.js +3 -3
- package/dist/classes/gedcom.d.ts +29 -0
- package/dist/classes/gedcom.d.ts.map +1 -1
- package/dist/classes/gedcom.js +180 -4
- package/dist/classes/index.js +18 -18
- package/dist/classes/indi.js +13 -13
- package/dist/classes/indis.d.ts.map +1 -1
- package/dist/classes/indis.js +42 -24
- package/dist/classes/list.js +6 -6
- package/dist/classes/name.js +2 -2
- package/dist/classes/note.js +2 -2
- package/dist/classes/obje.js +1 -1
- package/dist/classes/objes.js +1 -1
- package/dist/classes/repo.js +1 -1
- package/dist/classes/repos.js +1 -1
- package/dist/classes/sour.js +1 -1
- package/dist/classes/sours.js +1 -1
- package/dist/classes/subm.js +1 -1
- package/dist/classes/subms.js +1 -1
- package/dist/cli/commands/convert.d.ts +3 -0
- package/dist/cli/commands/convert.d.ts.map +1 -0
- package/dist/cli/commands/convert.js +83 -0
- package/dist/cli/commands/extract.d.ts +3 -0
- package/dist/cli/commands/extract.d.ts.map +1 -0
- package/dist/cli/commands/extract.js +85 -0
- package/dist/cli/commands/find.d.ts +3 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +97 -0
- package/dist/cli/commands/info.d.ts +3 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +80 -0
- package/dist/cli/commands/merge.d.ts +3 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +93 -0
- package/dist/cli/commands/relatives.d.ts +3 -0
- package/dist/cli/commands/relatives.d.ts.map +1 -0
- package/dist/cli/commands/relatives.js +107 -0
- package/dist/cli/commands/show.d.ts +3 -0
- package/dist/cli/commands/show.d.ts.map +1 -0
- package/dist/cli/commands/show.js +176 -0
- package/dist/cli/commands/stats.d.ts +3 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +59 -0
- package/dist/cli/commands/validate.d.ts +3 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +148 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +40 -0
- package/dist/cli/utils/formatters.d.ts +69 -0
- package/dist/cli/utils/formatters.d.ts.map +1 -0
- package/dist/cli/utils/formatters.js +125 -0
- package/dist/cli/utils/helpers.d.ts +21 -0
- package/dist/cli/utils/helpers.d.ts.map +1 -0
- package/dist/cli/utils/helpers.js +58 -0
- package/dist/constants/filters.js +1 -1
- package/dist/constants/index.js +3 -3
- package/dist/constants/orders.js +2 -2
- package/dist/factories/cache-factory.js +1 -1
- package/dist/factories/date-locale-factory.js +1 -1
- package/dist/factories/i18n-factory.js +1 -1
- package/dist/factories/index.js +4 -4
- package/dist/factories/kinship-factory.js +2 -2
- package/dist/factories/place-parser-provider.js +1 -1
- package/dist/factories/place-translator-provider.js +1 -1
- package/dist/index.js +37 -37
- package/dist/kinship-translator/index.js +9 -9
- package/dist/kinship-translator/kinship-translator.de.js +3 -3
- package/dist/kinship-translator/kinship-translator.en.js +4 -4
- package/dist/kinship-translator/kinship-translator.es.js +4 -4
- package/dist/kinship-translator/kinship-translator.fr.js +4 -4
- package/dist/kinship-translator/kinship-translator.hu.js +4 -4
- package/dist/kinship-translator/kinship-translator.js +1 -1
- package/dist/kinship-translator/translators.js +5 -5
- package/dist/types/index.js +3 -3
- package/dist/utils/cache.js +2 -2
- package/dist/utils/common-creator.js +11 -11
- package/dist/utils/date-formatter.js +3 -3
- package/dist/utils/get-places.js +1 -1
- package/dist/utils/get-product-details.js +1 -1
- package/dist/utils/index.js +16 -16
- package/dist/utils/name-formatter.js +2 -2
- package/dist/utils/nested-group.js +3 -3
- package/dist/utils/parser.js +14 -14
- package/package.json +13 -4
package/dist/classes/list.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import differenceBy from "lodash/differenceBy";
|
|
2
|
-
import get from "lodash/get";
|
|
3
|
-
import intersectionBy from "lodash/intersectionBy";
|
|
4
|
-
import set from "lodash/set";
|
|
5
|
-
import unset from "lodash/unset";
|
|
6
|
-
import { getValidTag } from "./common";
|
|
1
|
+
import differenceBy from "lodash-es/differenceBy.js";
|
|
2
|
+
import get from "lodash-es/get.js";
|
|
3
|
+
import intersectionBy from "lodash-es/intersectionBy.js";
|
|
4
|
+
import set from "lodash-es/set.js";
|
|
5
|
+
import unset from "lodash-es/unset.js";
|
|
6
|
+
import { getValidTag } from "./common.js";
|
|
7
7
|
export class List {
|
|
8
8
|
// length = 0;
|
|
9
9
|
constructor(items) {
|
package/dist/classes/name.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Common, createCommon, createProxy } from "./common";
|
|
2
|
-
import { Indi } from "./indi";
|
|
1
|
+
import { Common, createCommon, createProxy } from "./common.js";
|
|
2
|
+
import { Indi } from "./indi.js";
|
|
3
3
|
export class CommonName extends Common {
|
|
4
4
|
constructor(gedcom, id, main, parent) {
|
|
5
5
|
super(gedcom, id, main, parent);
|
package/dist/classes/note.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Common, createCommon, createProxy } from "./common";
|
|
2
|
-
import { List } from "./list";
|
|
1
|
+
import { Common, createCommon, createProxy } from "./common.js";
|
|
2
|
+
import { List } from "./list.js";
|
|
3
3
|
export class CommonNote extends Common {
|
|
4
4
|
constructor(gedcom, id, main, parent) {
|
|
5
5
|
super(gedcom, id, main, parent);
|
package/dist/classes/obje.js
CHANGED
package/dist/classes/objes.js
CHANGED
package/dist/classes/repo.js
CHANGED
package/dist/classes/repos.js
CHANGED
package/dist/classes/sour.js
CHANGED
package/dist/classes/sours.js
CHANGED
package/dist/classes/subm.js
CHANGED
package/dist/classes/subms.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiF7D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
|
+
import GedcomTree from '../../utils/parser.js';
|
|
3
|
+
import { formatSuccess, formatJson } from '../utils/formatters.js';
|
|
4
|
+
import { readGedcomFile, handleError, cleanGedcomName } from '../utils/helpers.js';
|
|
5
|
+
export function registerConvertCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('convert <file>')
|
|
8
|
+
.description('Convert GEDCOM to another format')
|
|
9
|
+
.requiredOption('-f, --format <format>', 'Output format: json, csv, markdown')
|
|
10
|
+
.option('-o, --output <file>', 'Output file path')
|
|
11
|
+
.action((file, options) => {
|
|
12
|
+
try {
|
|
13
|
+
const content = readGedcomFile(file);
|
|
14
|
+
const { gedcom: tree } = GedcomTree.parse(content);
|
|
15
|
+
const individuals = tree.indis();
|
|
16
|
+
let outputContent = '';
|
|
17
|
+
if (options.format === 'json') {
|
|
18
|
+
const jsonData = individuals.map(indi => ({
|
|
19
|
+
id: indi.id,
|
|
20
|
+
name: cleanGedcomName(indi.NAME?.toValue()),
|
|
21
|
+
sex: indi.SEX?.value || null,
|
|
22
|
+
birthDate: indi.BIRT?.DATE?.toValue() || null,
|
|
23
|
+
birthPlace: indi.BIRT?.PLAC?.value || null,
|
|
24
|
+
deathDate: indi.DEAT?.DATE?.toValue() || null,
|
|
25
|
+
deathPlace: indi.DEAT?.PLAC?.value || null,
|
|
26
|
+
}));
|
|
27
|
+
outputContent = formatJson(jsonData);
|
|
28
|
+
}
|
|
29
|
+
else if (options.format === 'csv') {
|
|
30
|
+
const lines = [];
|
|
31
|
+
lines.push('ID,Name,Sex,Birth Date,Birth Place,Death Date,Death Place');
|
|
32
|
+
individuals.forEach(indi => {
|
|
33
|
+
const csvEscape = (str) => {
|
|
34
|
+
if (!str)
|
|
35
|
+
return '';
|
|
36
|
+
if (str.includes(',') || str.includes('"')) {
|
|
37
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
38
|
+
}
|
|
39
|
+
return str;
|
|
40
|
+
};
|
|
41
|
+
lines.push([
|
|
42
|
+
csvEscape(indi.id),
|
|
43
|
+
csvEscape(cleanGedcomName(indi.NAME?.toValue())),
|
|
44
|
+
csvEscape(indi.SEX?.value),
|
|
45
|
+
csvEscape(indi.BIRT?.DATE?.toValue()),
|
|
46
|
+
csvEscape(indi.BIRT?.PLAC?.value),
|
|
47
|
+
csvEscape(indi.DEAT?.DATE?.toValue()),
|
|
48
|
+
csvEscape(indi.DEAT?.PLAC?.value),
|
|
49
|
+
].join(','));
|
|
50
|
+
});
|
|
51
|
+
outputContent = lines.join('\n');
|
|
52
|
+
}
|
|
53
|
+
else if (options.format === 'markdown') {
|
|
54
|
+
const lines = [];
|
|
55
|
+
lines.push('# GEDCOM Individuals\n');
|
|
56
|
+
lines.push('| ID | Name | Sex | Birth | Death |');
|
|
57
|
+
lines.push('|----|------|-----|-------|-------|');
|
|
58
|
+
individuals.forEach(indi => {
|
|
59
|
+
const name = cleanGedcomName(indi.NAME?.toValue()) || '?';
|
|
60
|
+
const sex = indi.SEX?.value || '?';
|
|
61
|
+
const birth = indi.BIRT?.DATE?.toValue() || '?';
|
|
62
|
+
const death = indi.DEAT?.DATE?.toValue() || '?';
|
|
63
|
+
lines.push(`| ${indi.id} | ${name} | ${sex} | ${birth} | ${death} |`);
|
|
64
|
+
});
|
|
65
|
+
outputContent = lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.error(`Unsupported format: ${options.format}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
if (options.output) {
|
|
72
|
+
writeFileSync(options.output, outputContent, 'utf-8');
|
|
73
|
+
console.log(formatSuccess(`Converted to ${options.format} and saved to ${options.output}`));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(outputContent);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
handleError(error, 'Failed to convert GEDCOM file');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4F7D"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
|
+
import GedcomTree from '../../utils/parser.js';
|
|
3
|
+
import { formatError, formatSuccess } from '../utils/formatters.js';
|
|
4
|
+
import { readGedcomFile, handleError, cleanGedcomName } from '../utils/helpers.js';
|
|
5
|
+
export function registerExtractCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('extract <file>')
|
|
8
|
+
.description('Extract a subset of individuals to a new GEDCOM file')
|
|
9
|
+
.requiredOption('-o, --output <file>', 'Output file path (required)')
|
|
10
|
+
.option('--surname <name>', 'Filter by surname')
|
|
11
|
+
.option('--birth-after <year>', 'Include individuals born after this year')
|
|
12
|
+
.option('--birth-before <year>', 'Include individuals born before this year')
|
|
13
|
+
.option('--death-after <year>', 'Include individuals who died after this year')
|
|
14
|
+
.option('--death-before <year>', 'Include individuals who died before this year')
|
|
15
|
+
.action((file, options) => {
|
|
16
|
+
try {
|
|
17
|
+
const content = readGedcomFile(file);
|
|
18
|
+
const { gedcom: tree } = GedcomTree.parse(content);
|
|
19
|
+
let individuals = tree.indis();
|
|
20
|
+
const results = [];
|
|
21
|
+
// Collect individuals that match all filters
|
|
22
|
+
individuals.forEach(indi => {
|
|
23
|
+
let matches = true;
|
|
24
|
+
// Filter by surname
|
|
25
|
+
if (options.surname && matches) {
|
|
26
|
+
const searchSurname = options.surname.toLowerCase();
|
|
27
|
+
const name = cleanGedcomName(indi.NAME?.toValue()).toLowerCase();
|
|
28
|
+
matches = name.includes(searchSurname);
|
|
29
|
+
}
|
|
30
|
+
// Filter by birth year
|
|
31
|
+
if (options.birthAfter && matches) {
|
|
32
|
+
const year = parseInt(options.birthAfter, 10);
|
|
33
|
+
const birthDate = indi.BIRT?.DATE?.toValue();
|
|
34
|
+
const match = birthDate?.match(/\d{4}/);
|
|
35
|
+
matches = match && parseInt(match[0], 10) > year;
|
|
36
|
+
}
|
|
37
|
+
if (options.birthBefore && matches) {
|
|
38
|
+
const year = parseInt(options.birthBefore, 10);
|
|
39
|
+
const birthDate = indi.BIRT?.DATE?.toValue();
|
|
40
|
+
const match = birthDate?.match(/\d{4}/);
|
|
41
|
+
matches = match && parseInt(match[0], 10) < year;
|
|
42
|
+
}
|
|
43
|
+
// Filter by death year
|
|
44
|
+
if (options.deathAfter && matches) {
|
|
45
|
+
const year = parseInt(options.deathAfter, 10);
|
|
46
|
+
const deathDate = indi.DEAT?.DATE?.toValue();
|
|
47
|
+
const match = deathDate?.match(/\d{4}/);
|
|
48
|
+
matches = match && parseInt(match[0], 10) > year;
|
|
49
|
+
}
|
|
50
|
+
if (options.deathBefore && matches) {
|
|
51
|
+
const year = parseInt(options.deathBefore, 10);
|
|
52
|
+
const deathDate = indi.DEAT?.DATE?.toValue();
|
|
53
|
+
const match = deathDate?.match(/\d{4}/);
|
|
54
|
+
matches = match && parseInt(match[0], 10) < year;
|
|
55
|
+
}
|
|
56
|
+
if (matches) {
|
|
57
|
+
results.push(indi);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
if (results.length === 0) {
|
|
61
|
+
console.log(formatError('No individuals match the criteria'));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
// Create subset GEDCOM
|
|
65
|
+
const lines = [];
|
|
66
|
+
lines.push('0 HEAD');
|
|
67
|
+
lines.push('1 SOUR gedcom-parser CLI');
|
|
68
|
+
lines.push('1 GEDC');
|
|
69
|
+
lines.push('2 VERS 5.5.1');
|
|
70
|
+
lines.push('1 CHAR UTF-8');
|
|
71
|
+
results.forEach(indi => {
|
|
72
|
+
const raw = indi.raw();
|
|
73
|
+
if (raw) {
|
|
74
|
+
lines.push(...raw.split('\n').filter(line => line.trim()));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
lines.push('0 TRLR');
|
|
78
|
+
writeFileSync(options.output, lines.join('\n'), 'utf-8');
|
|
79
|
+
console.log(formatSuccess(`Extracted ${results.length} individuals to ${options.output}`));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
handleError(error, 'Failed to extract individuals');
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/find.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0G1D"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import GedcomTree from '../../utils/parser.js';
|
|
2
|
+
import { formatHeader, formatListItem, formatJson, formatId, formatName, formatDate, formatWarning, } from '../utils/formatters.js';
|
|
3
|
+
import { readGedcomFile, handleError, cleanGedcomName, formatLifespan } from '../utils/helpers.js';
|
|
4
|
+
export function registerFindCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('find <file> [query]')
|
|
7
|
+
.description('Find individuals in a GEDCOM file')
|
|
8
|
+
.option('--id <id>', 'Find by GEDCOM ID')
|
|
9
|
+
.option('--name <name>', 'Find by name (substring search)')
|
|
10
|
+
.option('--birth-year <year>', 'Filter by birth year')
|
|
11
|
+
.option('--death-year <year>', 'Filter by death year')
|
|
12
|
+
.option('-j, --json', 'Output in JSON format')
|
|
13
|
+
.action((file, query, options) => {
|
|
14
|
+
try {
|
|
15
|
+
const content = readGedcomFile(file);
|
|
16
|
+
const { gedcom: tree } = GedcomTree.parse(content);
|
|
17
|
+
const individuals = tree.indis();
|
|
18
|
+
const results = [];
|
|
19
|
+
// Collect all individuals that match filters
|
|
20
|
+
individuals.forEach((indi) => {
|
|
21
|
+
let matches = true;
|
|
22
|
+
// Filter by ID
|
|
23
|
+
if (options.id && indi.id !== options.id) {
|
|
24
|
+
matches = false;
|
|
25
|
+
}
|
|
26
|
+
// Filter by name (substring search, case insensitive)
|
|
27
|
+
if ((options.name || query) && matches) {
|
|
28
|
+
const searchName = (options.name || query || '').toLowerCase();
|
|
29
|
+
const name = cleanGedcomName(indi.NAME?.toValue()).toLowerCase();
|
|
30
|
+
if (!name.includes(searchName)) {
|
|
31
|
+
matches = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Filter by birth year
|
|
35
|
+
if (options.birthYear && matches) {
|
|
36
|
+
const year = options.birthYear;
|
|
37
|
+
const birthDate = indi.BIRT?.DATE?.toValue();
|
|
38
|
+
if (!birthDate?.includes(String(year))) {
|
|
39
|
+
matches = false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Filter by death year
|
|
43
|
+
if (options.deathYear && matches) {
|
|
44
|
+
const year = options.deathYear;
|
|
45
|
+
const deathDate = indi.DEAT?.DATE?.toValue();
|
|
46
|
+
if (!deathDate?.includes(String(year))) {
|
|
47
|
+
matches = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (matches) {
|
|
51
|
+
results.push(indi);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (options.json) {
|
|
55
|
+
const jsonResults = results.map(indi => ({
|
|
56
|
+
id: indi.id,
|
|
57
|
+
name: cleanGedcomName(indi.NAME?.toValue()),
|
|
58
|
+
birthDate: indi.BIRT?.DATE?.toValue() || null,
|
|
59
|
+
birthPlace: indi.BIRT?.PLAC?.value || null,
|
|
60
|
+
deathDate: indi.DEAT?.DATE?.toValue() || null,
|
|
61
|
+
deathPlace: indi.DEAT?.PLAC?.value || null,
|
|
62
|
+
sex: indi.SEX?.value || null,
|
|
63
|
+
}));
|
|
64
|
+
console.log(formatJson({ count: jsonResults.length, individuals: jsonResults }));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (results.length === 0) {
|
|
68
|
+
console.log(formatWarning('No individuals found matching the criteria'));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(formatHeader(`Found ${results.length} individual(s)\n`));
|
|
72
|
+
results.forEach(indi => {
|
|
73
|
+
const name = cleanGedcomName(indi.NAME?.toValue());
|
|
74
|
+
const birthDate = indi.BIRT?.DATE?.toValue();
|
|
75
|
+
const deathDate = indi.DEAT?.DATE?.toValue();
|
|
76
|
+
const lifespan = formatLifespan(birthDate, deathDate);
|
|
77
|
+
console.log(formatListItem(`${formatId(indi.id)} ${formatName(name)} ${lifespan}`));
|
|
78
|
+
// Show birth place if available
|
|
79
|
+
const birthPlace = indi.BIRT?.PLAC?.value;
|
|
80
|
+
if (birthPlace) {
|
|
81
|
+
console.log(formatListItem(`Birth: ${formatDate(birthDate)} in ${birthPlace}`, 1));
|
|
82
|
+
}
|
|
83
|
+
// Show death place if available
|
|
84
|
+
const deathPlace = indi.DEAT?.PLAC?.value;
|
|
85
|
+
if (deathPlace) {
|
|
86
|
+
console.log(formatListItem(`Death: ${formatDate(deathDate)} in ${deathPlace}`, 1));
|
|
87
|
+
}
|
|
88
|
+
console.log();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
handleError(error, 'Failed to search GEDCOM file');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkF1D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import GedcomTree from '../../utils/parser.js';
|
|
2
|
+
import { formatHeader, formatLabel, formatValue, formatCount, formatJson, formatSuccess, } from '../utils/formatters.js';
|
|
3
|
+
import { readGedcomFile, handleError } from '../utils/helpers.js';
|
|
4
|
+
export function registerInfoCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('info <file>')
|
|
7
|
+
.description('Display basic information about a GEDCOM file')
|
|
8
|
+
.option('-j, --json', 'Output in JSON format')
|
|
9
|
+
.option('-v, --verbose', 'Show detailed information')
|
|
10
|
+
.action((file, options) => {
|
|
11
|
+
try {
|
|
12
|
+
const content = readGedcomFile(file);
|
|
13
|
+
const { gedcom: tree } = GedcomTree.parse(content);
|
|
14
|
+
const individuals = tree.indis();
|
|
15
|
+
const families = tree.fams();
|
|
16
|
+
const sources = tree.sours();
|
|
17
|
+
const repos = tree.repos();
|
|
18
|
+
const objes = tree.objes();
|
|
19
|
+
const submitters = tree.subms();
|
|
20
|
+
// Get GEDCOM version from header
|
|
21
|
+
const header = tree.HEAD;
|
|
22
|
+
const version = header?.GEDC?.VERS?.value || 'Unknown';
|
|
23
|
+
const info = {
|
|
24
|
+
file,
|
|
25
|
+
version,
|
|
26
|
+
individuals: individuals?.length || 0,
|
|
27
|
+
families: families?.length || 0,
|
|
28
|
+
sources: sources?.length || 0,
|
|
29
|
+
repositories: repos?.length || 0,
|
|
30
|
+
mediaObjects: objes?.length || 0,
|
|
31
|
+
submitters: submitters?.length || 0,
|
|
32
|
+
};
|
|
33
|
+
if (options.json) {
|
|
34
|
+
console.log(formatJson(info));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(formatSuccess('GEDCOM file parsed successfully\n'));
|
|
38
|
+
console.log(formatHeader('File Information'));
|
|
39
|
+
console.log(`${formatLabel('File')} ${formatValue(file)}`);
|
|
40
|
+
console.log(`${formatLabel('GEDCOM Version')} ${formatValue(version)}`);
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(formatHeader('Statistics'));
|
|
43
|
+
console.log(`${formatLabel('Individuals')} ${formatCount(individuals?.length || 0)}`);
|
|
44
|
+
console.log(`${formatLabel('Families')} ${formatCount(families?.length || 0)}`);
|
|
45
|
+
console.log(`${formatLabel('Sources')} ${formatCount(sources?.length || 0)}`);
|
|
46
|
+
console.log(`${formatLabel('Repositories')} ${formatCount(repos?.length || 0)}`);
|
|
47
|
+
console.log(`${formatLabel('Media Objects')} ${formatCount(objes?.length || 0)}`);
|
|
48
|
+
console.log(`${formatLabel('Submitters')} ${formatCount(submitters?.length || 0)}`);
|
|
49
|
+
if (options.verbose) {
|
|
50
|
+
console.log();
|
|
51
|
+
console.log(formatHeader('Additional Details'));
|
|
52
|
+
// Most common surnames
|
|
53
|
+
const surnames = new Map();
|
|
54
|
+
individuals.forEach((indi) => {
|
|
55
|
+
const name = indi.NAME?.toValue();
|
|
56
|
+
if (name) {
|
|
57
|
+
const match = name.match(/\/(.+?)\//);
|
|
58
|
+
if (match) {
|
|
59
|
+
const surname = match[1];
|
|
60
|
+
surnames.set(surname, (surnames.get(surname) || 0) + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const topSurnames = Array.from(surnames.entries())
|
|
65
|
+
.sort((a, b) => b[1] - a[1])
|
|
66
|
+
.slice(0, 5);
|
|
67
|
+
if (topSurnames.length > 0) {
|
|
68
|
+
console.log(`${formatLabel('Most Common Surnames')}`);
|
|
69
|
+
topSurnames.forEach(([surname, count]) => {
|
|
70
|
+
console.log(` - ${surname}: ${formatCount(count)}`);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
handleError(error, 'Failed to parse GEDCOM file');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgH3D"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
|
+
import GedcomTree from '../../utils/parser.js';
|
|
3
|
+
import { formatSuccess } from '../utils/formatters.js';
|
|
4
|
+
import { readGedcomFile, handleError } from '../utils/helpers.js';
|
|
5
|
+
export function registerMergeCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('merge <files...>')
|
|
8
|
+
.description('Merge multiple GEDCOM files')
|
|
9
|
+
.requiredOption('-o, --output <file>', 'Output file path (required)')
|
|
10
|
+
.option('--dedupe', 'Attempt to detect and merge duplicates (basic implementation)')
|
|
11
|
+
.action((files, options) => {
|
|
12
|
+
try {
|
|
13
|
+
if (files.length < 2) {
|
|
14
|
+
console.error('At least 2 files are required for merging');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const allIndividuals = [];
|
|
18
|
+
const allFamilies = [];
|
|
19
|
+
const seenIds = new Set();
|
|
20
|
+
let idCounter = 1;
|
|
21
|
+
// Parse all files
|
|
22
|
+
files.forEach(file => {
|
|
23
|
+
const content = readGedcomFile(file);
|
|
24
|
+
const { gedcom: tree } = GedcomTree.parse(content);
|
|
25
|
+
const individuals = tree.indis();
|
|
26
|
+
const families = tree.fams();
|
|
27
|
+
// Add individuals with unique IDs
|
|
28
|
+
individuals.forEach(indi => {
|
|
29
|
+
let id = indi.id;
|
|
30
|
+
// If dedupe is enabled, check for duplicates (basic check by name and birth date)
|
|
31
|
+
if (options.dedupe) {
|
|
32
|
+
const name = indi.NAME?.toValue();
|
|
33
|
+
const birthDate = indi.BIRT?.DATE?.toValue();
|
|
34
|
+
const duplicate = allIndividuals.find(existing => {
|
|
35
|
+
return existing.NAME?.toValue() === name &&
|
|
36
|
+
existing.BIRT?.DATE?.toValue() === birthDate;
|
|
37
|
+
});
|
|
38
|
+
if (duplicate) {
|
|
39
|
+
// Skip duplicate
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Ensure unique ID
|
|
44
|
+
while (seenIds.has(id)) {
|
|
45
|
+
id = `@I${idCounter++}@`;
|
|
46
|
+
}
|
|
47
|
+
seenIds.add(id);
|
|
48
|
+
// Store with original object (we'll need to update ID in output)
|
|
49
|
+
allIndividuals.push({ ...indi, newId: id });
|
|
50
|
+
});
|
|
51
|
+
// Add families with unique IDs
|
|
52
|
+
families.forEach(fam => {
|
|
53
|
+
let id = fam.id;
|
|
54
|
+
while (seenIds.has(id)) {
|
|
55
|
+
id = `@F${idCounter++}@`;
|
|
56
|
+
}
|
|
57
|
+
seenIds.add(id);
|
|
58
|
+
allFamilies.push({ ...fam, newId: id });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// Create merged GEDCOM
|
|
62
|
+
const lines = [];
|
|
63
|
+
lines.push('0 HEAD');
|
|
64
|
+
lines.push('1 SOUR gedcom-parser CLI');
|
|
65
|
+
lines.push('1 GEDC');
|
|
66
|
+
lines.push('2 VERS 5.5.1');
|
|
67
|
+
lines.push('1 CHAR UTF-8');
|
|
68
|
+
// Add all individuals
|
|
69
|
+
allIndividuals.forEach(indi => {
|
|
70
|
+
const raw = indi.raw?.() || '';
|
|
71
|
+
if (raw) {
|
|
72
|
+
// Replace old ID with new ID if needed
|
|
73
|
+
const updatedRaw = raw.replace(new RegExp(`^0 ${indi.id} INDI`, 'm'), `0 ${indi.newId} INDI`);
|
|
74
|
+
lines.push(...updatedRaw.split('\n').filter(line => line.trim()));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Add all families
|
|
78
|
+
allFamilies.forEach(fam => {
|
|
79
|
+
const raw = fam.raw?.() || '';
|
|
80
|
+
if (raw) {
|
|
81
|
+
const updatedRaw = raw.replace(new RegExp(`^0 ${fam.id} FAM`, 'm'), `0 ${fam.newId} FAM`);
|
|
82
|
+
lines.push(...updatedRaw.split('\n').filter(line => line.trim()));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
lines.push('0 TRLR');
|
|
86
|
+
writeFileSync(options.output, lines.join('\n'), 'utf-8');
|
|
87
|
+
console.log(formatSuccess(`Merged ${files.length} files (${allIndividuals.length} individuals, ${allFamilies.length} families) into ${options.output}`));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
handleError(error, 'Failed to merge GEDCOM files');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relatives.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/relatives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwF/D"}
|