@larsgw/formica 0.8.4 → 0.8.5
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/.github/workflows/ci.yml +1 -1
- package/CHANGELOG.md +19 -0
- package/lib/bin/generate-linked-data.js +9 -5
- package/lib/bin/process-resources.js +1 -1
- package/lib/bin/validate-resources-text.js +1 -1
- package/lib/resources/parse-name.d.ts +2 -0
- package/lib/resources/parse-name.js +352 -0
- package/lib/resources/parse-text.js +118 -409
- package/package.json +2 -1
- package/src/bin/generate-linked-data.ts +8 -5
- package/src/bin/process-resources.ts +1 -1
- package/src/bin/validate-resources-text.ts +1 -1
- package/src/module.d.ts +4 -2
- package/src/resources/parse-name.ts +374 -0
- package/src/resources/parse-text.ts +126 -440
- package/test/resources.js +99 -17
package/.github/workflows/ci.yml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [0.8.5](https://github.com/identification-resources/formica/compare/v0.8.4...v0.8.5) (2025-10-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **catalog:** add mapping for typo 'male' in linked data ([a83d34e](https://github.com/identification-resources/formica/commit/a83d34e7aa57a78e410e2ea55adbf75e522d9028))
|
|
7
|
+
* **catalog:** fix dcmitype: for algorithms, checklists ([68e353d](https://github.com/identification-resources/formica/commit/68e353d71d632e092e3e79254e6b4f575c004f1c))
|
|
8
|
+
* **catalog:** fix typos in linked data generation ([9ad951f](https://github.com/identification-resources/formica/commit/9ad951f5c19978509c6aba7fd81470270707da99))
|
|
9
|
+
* **resources:** add scientific name for complexes, aggregates ([843afc3](https://github.com/identification-resources/formica/commit/843afc39df1952dda4bb6339ee47f2619432bb74))
|
|
10
|
+
* **resources:** omit generic error stack trace ([c831bc2](https://github.com/identification-resources/formica/commit/c831bc2a952ef17a9e57b012fe0568e2acc0c6ae))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **resources:** handle subgenus synonyms of genera ([4e61336](https://github.com/identification-resources/formica/commit/4e61336e36054ffd6df57ebad5479e7ceebcb235)), closes [#16](https://github.com/identification-resources/formica/issues/16)
|
|
16
|
+
* **resources:** rework parsing of resources ([cff7730](https://github.com/identification-resources/formica/commit/cff773000c5b9e3f990c619cdd45a76a18ee39a9)), closes [#15](https://github.com/identification-resources/formica/issues/15) [#7](https://github.com/identification-resources/formica/issues/7)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
1
20
|
## [0.8.4](https://github.com/identification-resources/formica/compare/v0.8.3...v0.8.4) (2025-09-19)
|
|
2
21
|
|
|
3
22
|
|
|
@@ -126,6 +126,7 @@ var SCOPES = {
|
|
|
126
126
|
// sex
|
|
127
127
|
'females': ['dwciri:sex', 'http://rs.gbif.org/vocabulary/gbif/sex/female'],
|
|
128
128
|
'males': ['dwciri:sex', 'http://rs.gbif.org/vocabulary/gbif/sex/male'],
|
|
129
|
+
'male': ['dwciri:sex', 'http://rs.gbif.org/vocabulary/gbif/sex/male'],
|
|
129
130
|
// caste
|
|
130
131
|
'queens': ['dwc:caste', 'queen'],
|
|
131
132
|
'workers': ['dwc:caste', 'worker'],
|
|
@@ -278,7 +279,7 @@ function makeLinkedDataForTaxon(taxon) {
|
|
|
278
279
|
var node = {
|
|
279
280
|
'@id': "".concat(PREFIX, "taxon/").concat(taxon.get('id')),
|
|
280
281
|
'@type': 'dwc:Taxon',
|
|
281
|
-
'dwc:scientificName': taxon.get('
|
|
282
|
+
'dwc:scientificName': taxon.get('name'),
|
|
282
283
|
};
|
|
283
284
|
if (taxon.has('rank')) {
|
|
284
285
|
node['dwc:taxonRank'] = makeTaxonRankUri(taxon.get('rank'));
|
|
@@ -372,7 +373,7 @@ function makeLinkedDataForResource(work, files, resourceId) {
|
|
|
372
373
|
}
|
|
373
374
|
var node = __assign(__assign({}, makeLinkedDataForWork(resource, files)), { '@id': "".concat(PREFIX, "resource/").concat(resourceId), '@type': 'bibo:DocumentPart' });
|
|
374
375
|
var types = (_b = (_a = resource.get('key_type')) !== null && _a !== void 0 ? _a : work.get('key_type')) !== null && _b !== void 0 ? _b : [];
|
|
375
|
-
if (types.includes('matrix')) {
|
|
376
|
+
if (types.includes('matrix') || types.includes('algorithm')) {
|
|
376
377
|
node['dcterms:type'] = { '@id': 'http://purl.org/dc/dcmitype/Software' };
|
|
377
378
|
}
|
|
378
379
|
else if (types.includes('key') || types.includes('reference') || types.includes('supplement')) {
|
|
@@ -381,6 +382,9 @@ function makeLinkedDataForResource(work, files, resourceId) {
|
|
|
381
382
|
else if (types.includes('gallery') || types.includes('collection')) {
|
|
382
383
|
node['dcterms:type'] = { '@id': 'http://purl.org/dc/dcmitype/Collection' };
|
|
383
384
|
}
|
|
385
|
+
else if (types.includes('checklist')) {
|
|
386
|
+
node['dcterms:type'] = { '@id': 'http://purl.org/dc/dcmitype/Dataset' };
|
|
387
|
+
}
|
|
384
388
|
if (types.includes('key') || types.includes('matrix')) {
|
|
385
389
|
node['ac:subtype'] = { '@id': 'http://rs.tdwg.org/acsubtype/values/IdentificationKey' };
|
|
386
390
|
}
|
|
@@ -637,10 +641,10 @@ function makeLinkedDataForWorks(files) {
|
|
|
637
641
|
node['dcterms:hasPart'] = [];
|
|
638
642
|
}
|
|
639
643
|
node['dcterms:hasPart'].push(resource);
|
|
640
|
-
if (!Array.isArray(resource['dcterms:
|
|
641
|
-
resource['dcterms:
|
|
644
|
+
if (!Array.isArray(resource['dcterms:isPartOf'])) {
|
|
645
|
+
resource['dcterms:isPartOf'] = [];
|
|
642
646
|
}
|
|
643
|
-
resource['dcterms:
|
|
647
|
+
resource['dcterms:isPartOf'].push({ '@id': node['@id'] });
|
|
644
648
|
}
|
|
645
649
|
};
|
|
646
650
|
for (var _b = 0, _c = works.entities; _b < _c.length; _b++) {
|
|
@@ -411,7 +411,7 @@ var ResourceProcessor = /** @class */ (function () {
|
|
|
411
411
|
return [2 /*return*/, resources.parseTextFile(file, id, old)];
|
|
412
412
|
case 10:
|
|
413
413
|
error_1 = _g.sent();
|
|
414
|
-
console.log(error_1);
|
|
414
|
+
console.log(error_1.message);
|
|
415
415
|
return [4 /*yield*/, (0, util_1.prompt)("".concat(id, ": generating Darwin Core failed, retry? "))
|
|
416
416
|
// Clear cache to re-import
|
|
417
417
|
];
|
|
@@ -94,7 +94,7 @@ function main(args) {
|
|
|
94
94
|
index_1.resources.parseTextFile(file, id);
|
|
95
95
|
}
|
|
96
96
|
catch (error) {
|
|
97
|
-
console.error(filePath + '\n
|
|
97
|
+
console.error(filePath + '\n' + error.message.replace(/^/gm, ' ') + '\n');
|
|
98
98
|
exitStatus = 1;
|
|
99
99
|
}
|
|
100
100
|
_a.label = 3;
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RANKS = void 0;
|
|
4
|
+
exports.parseName = parseName;
|
|
5
|
+
exports.RANKS = [
|
|
6
|
+
'phylum',
|
|
7
|
+
'subphylum',
|
|
8
|
+
'class',
|
|
9
|
+
'infraclass',
|
|
10
|
+
'superorder',
|
|
11
|
+
'order',
|
|
12
|
+
'suborder',
|
|
13
|
+
'infraorder',
|
|
14
|
+
'superfamily',
|
|
15
|
+
'family',
|
|
16
|
+
'subfamily',
|
|
17
|
+
'tribe',
|
|
18
|
+
'subtribe',
|
|
19
|
+
'genus',
|
|
20
|
+
'subgenus',
|
|
21
|
+
'section', // not ICZN
|
|
22
|
+
'subsection', // not ICZN
|
|
23
|
+
'series', // not ICZN
|
|
24
|
+
'group',
|
|
25
|
+
'subgroup', // ...
|
|
26
|
+
'aggregate', // not ICZN
|
|
27
|
+
'complex', // not ICZN
|
|
28
|
+
'species',
|
|
29
|
+
'subspecies',
|
|
30
|
+
'variety',
|
|
31
|
+
'form',
|
|
32
|
+
'aberration', // not ICZN
|
|
33
|
+
'race', // not ICZN
|
|
34
|
+
'stirps' // not ICZN
|
|
35
|
+
];
|
|
36
|
+
var TAXONOMIC_STATUS = {
|
|
37
|
+
'>': 'incorrect',
|
|
38
|
+
'+': 'heterotypic synonym',
|
|
39
|
+
'=': 'synonym'
|
|
40
|
+
};
|
|
41
|
+
var RANK_LABELS = {
|
|
42
|
+
'subspecies': 'subsp.',
|
|
43
|
+
'variety': 'var.',
|
|
44
|
+
'form': 'f.',
|
|
45
|
+
'aberration': 'ab.',
|
|
46
|
+
'race': 'r.',
|
|
47
|
+
'stirps': 'st.'
|
|
48
|
+
};
|
|
49
|
+
var RANK_LABELS_REVERSE = {
|
|
50
|
+
'st': 'stirps',
|
|
51
|
+
'r': 'race',
|
|
52
|
+
'ab': 'aberration',
|
|
53
|
+
'f': 'form',
|
|
54
|
+
'var': 'variety',
|
|
55
|
+
'ssp': 'subspecies',
|
|
56
|
+
'subsp': 'subspecies'
|
|
57
|
+
};
|
|
58
|
+
var HYBRID_SIGN = '\u00D7';
|
|
59
|
+
/**
|
|
60
|
+
* 1. Any number of
|
|
61
|
+
* - capitalized words
|
|
62
|
+
* - "&"
|
|
63
|
+
* - " in "
|
|
64
|
+
* - " ex "
|
|
65
|
+
* - lowercase name particles
|
|
66
|
+
* 2. Followed by a capitalized word
|
|
67
|
+
* 3. Optionally, followed by "et al."
|
|
68
|
+
*/
|
|
69
|
+
var LOWERCASE_NAME_PARTICLES = ['y', 'der', 'den', 'de', 'van', 'von'].join('|');
|
|
70
|
+
var SIMPLE_AUTHOR_PATTERN = '(?:(?:\\p{Lu}\\S*|&|in|ex|' + LOWERCASE_NAME_PARTICLES + ')\\s*)*\\p{Lu}\\S+(?:\\s+et\\s+al\\.)?';
|
|
71
|
+
var NAME_PATTERN = new RegExp('^' +
|
|
72
|
+
// $1 main name part
|
|
73
|
+
'(\\S+)' +
|
|
74
|
+
// $2 optional author citation
|
|
75
|
+
'(?: ' +
|
|
76
|
+
// but not auct(t)., etc.
|
|
77
|
+
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom|gen|subgen)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
78
|
+
'(' +
|
|
79
|
+
// $2.1 anything in parentheses, followed by optional revising author(s)
|
|
80
|
+
'\\(.+?\\)(?:\\s+' + SIMPLE_AUTHOR_PATTERN + ')?' +
|
|
81
|
+
'|' +
|
|
82
|
+
// $2.2 anything followed by a year
|
|
83
|
+
'.+?\\d{4}\\)?' +
|
|
84
|
+
'|' +
|
|
85
|
+
// $2.3 author(s)
|
|
86
|
+
SIMPLE_AUTHOR_PATTERN +
|
|
87
|
+
'))?' +
|
|
88
|
+
// $3 optional notes
|
|
89
|
+
'(?:,? (.+))?' +
|
|
90
|
+
'$', 'u');
|
|
91
|
+
/**
|
|
92
|
+
* Structure
|
|
93
|
+
* $1 genus: ((?:x )?[A-Z]\S+)
|
|
94
|
+
* $2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
95
|
+
*/
|
|
96
|
+
var SUBGENUS_PATTERN = /^([A-Z]\S+) (?:\(([A-Z]\S+?)\))(?= |$)/;
|
|
97
|
+
/**
|
|
98
|
+
* Structure
|
|
99
|
+
* $1 genus+subgenus (+ trailing space): (?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?
|
|
100
|
+
* $1.1 genus: ((?:x )?[A-Z]\S+)
|
|
101
|
+
* $1.2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
102
|
+
* $2 species: (x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)
|
|
103
|
+
* $2a: x [a-z-]+
|
|
104
|
+
* $2b hybrid: [a-z-][^\s.]+(?: x [a-z-]+)?
|
|
105
|
+
* $2c intergeneric hybrid: [A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+
|
|
106
|
+
*/
|
|
107
|
+
var BINAME_PATTERN = /^(?:((?:x )?[A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?(x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)(?= |$)/;
|
|
108
|
+
function compareRanks(a, b) {
|
|
109
|
+
return exports.RANKS.indexOf(a) - exports.RANKS.indexOf(b);
|
|
110
|
+
}
|
|
111
|
+
function capitalize(name) {
|
|
112
|
+
return name[0].toUpperCase() + name.slice(1).toLowerCase();
|
|
113
|
+
}
|
|
114
|
+
function capitalizeGenericName(name) {
|
|
115
|
+
if (name[0] === HYBRID_SIGN) {
|
|
116
|
+
return HYBRID_SIGN + capitalize(name.slice(1));
|
|
117
|
+
}
|
|
118
|
+
return capitalize(name);
|
|
119
|
+
}
|
|
120
|
+
function isUpperCase(name) {
|
|
121
|
+
return name === name.toUpperCase();
|
|
122
|
+
}
|
|
123
|
+
function getSynonymRank(name, rank) {
|
|
124
|
+
var rest = name.replace(BINAME_PATTERN, '');
|
|
125
|
+
var rankPrefix = rest.match(/^(?: |^)(st|r|ab|f|var|ssp|subsp)\. /);
|
|
126
|
+
if (rankPrefix) {
|
|
127
|
+
return RANK_LABELS_REVERSE[rankPrefix[1]];
|
|
128
|
+
}
|
|
129
|
+
else if (SUBGENUS_PATTERN.test(name)) {
|
|
130
|
+
return 'subgenus';
|
|
131
|
+
}
|
|
132
|
+
else if (!BINAME_PATTERN.test(name)) {
|
|
133
|
+
return rank;
|
|
134
|
+
}
|
|
135
|
+
else if (/^ (?!sensu)[a-z0-9-]+($| )/.test(rest)) {
|
|
136
|
+
return 'subspecies';
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
return 'species';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function capitalizeAuthors(authors) {
|
|
143
|
+
return authors
|
|
144
|
+
.replace(/[^\x00-\x40\x5B-\x60\x7B-\x7F]+/g, // eslint-disable-line no-control-regex
|
|
145
|
+
function (// eslint-disable-line no-control-regex
|
|
146
|
+
name) { return isUpperCase(name) ? capitalize(name) : name; })
|
|
147
|
+
.replace(/ Y /g, ' y ');
|
|
148
|
+
}
|
|
149
|
+
function parseName(name, rank, parent) {
|
|
150
|
+
var _a, _b;
|
|
151
|
+
var item = {};
|
|
152
|
+
// Synonyms have the accepted name usage as 'parent'.
|
|
153
|
+
var isSynonym = /^[+=>] /.test(name);
|
|
154
|
+
if (isSynonym) {
|
|
155
|
+
item.taxonomicStatus = TAXONOMIC_STATUS[name[0]];
|
|
156
|
+
name = name.replace(/^[+=>] (\? ?)?/, '');
|
|
157
|
+
rank = getSynonymRank(name, parent.taxonRank);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
item.taxonomicStatus = 'accepted';
|
|
161
|
+
}
|
|
162
|
+
// Clusters
|
|
163
|
+
if (/^\[(_|\d+)\] /.test(name)) {
|
|
164
|
+
name = name.replace(/^\[(_|\d+)\] /, '');
|
|
165
|
+
}
|
|
166
|
+
// Set verbatim identification after subsequent syntax is removed.
|
|
167
|
+
item.verbatimIdentification = name.replace(/(?<=^| )x(?=$| )/g, HYBRID_SIGN).replace(/_/g, ' ');
|
|
168
|
+
// Parent context is used for parsing and formatting binomial names.
|
|
169
|
+
// For formatting, it needs to match external databases (i.e. be correct).
|
|
170
|
+
// For parsing, it needs to match the current file. If relevant parents
|
|
171
|
+
// (i.e. genus, species) had mistakes that were corrected, the uncorrected
|
|
172
|
+
// genus and species names need to be used.
|
|
173
|
+
var parentContext = {
|
|
174
|
+
genus: parent.genus,
|
|
175
|
+
subgenus: parent.subgenus,
|
|
176
|
+
specificEpithet: parent.specificEpithet,
|
|
177
|
+
incorrect: {
|
|
178
|
+
genus: parent.incorrect && parent.incorrect.genus,
|
|
179
|
+
specificEpithet: parent.incorrect && parent.incorrect.specificEpithet
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
// Both contexts should be amended in the two cases where binomial names
|
|
183
|
+
// are fully used: (1) synonyms and (2) multinomial taxa without parents to
|
|
184
|
+
// provide parts of the name (e.g. bare species without a genus parent, or
|
|
185
|
+
// even subspecies without a species or genus parent).
|
|
186
|
+
if (isSynonym || !parentContext.genus || (compareRanks('species', rank) < 0 && !parentContext.specificEpithet)) {
|
|
187
|
+
var _c = (_b = (_a = name.match(BINAME_PATTERN)) !== null && _a !== void 0 ? _a : name.match(SUBGENUS_PATTERN)) !== null && _b !== void 0 ? _b : [], genus = _c[1], subgenus = _c[2], species = _c[3];
|
|
188
|
+
if (genus) {
|
|
189
|
+
parentContext.incorrect.genus = genus;
|
|
190
|
+
parentContext.genus = capitalizeGenericName(genus.replace(/(^| )x /, HYBRID_SIGN));
|
|
191
|
+
}
|
|
192
|
+
if (subgenus) {
|
|
193
|
+
parentContext.subgenus = capitalize(subgenus);
|
|
194
|
+
}
|
|
195
|
+
else if (genus) {
|
|
196
|
+
// If a genus is given but no subgenus, remove any existing subgenus
|
|
197
|
+
// from the parent context.
|
|
198
|
+
delete parentContext.subgenus;
|
|
199
|
+
}
|
|
200
|
+
if (species && compareRanks('species', rank) < 0) {
|
|
201
|
+
parentContext.incorrect.specificEpithet = species;
|
|
202
|
+
parentContext.specificEpithet = species.replace(/(^| )x /, HYBRID_SIGN);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// In taxa of group, species or lower, the name should just contain the
|
|
206
|
+
// (infra)specific epithet and the author information & remarks when processing
|
|
207
|
+
// further.
|
|
208
|
+
if (compareRanks('group', rank) <= 0) {
|
|
209
|
+
// Remove genus
|
|
210
|
+
var genus = parentContext.incorrect.genus || parentContext.genus || '';
|
|
211
|
+
if (name[0] === genus[0] && name.toLowerCase().startsWith(genus.toLowerCase() + ' ')) {
|
|
212
|
+
name = name.slice(genus.length + 1);
|
|
213
|
+
}
|
|
214
|
+
// Remove subgenus
|
|
215
|
+
name = name.replace(/^\(.*?\) /, '');
|
|
216
|
+
// Infraspecific taxa
|
|
217
|
+
if (compareRanks('species', rank) < 0) {
|
|
218
|
+
// Remove specific epithet
|
|
219
|
+
var species = parentContext.incorrect.specificEpithet || parentContext.specificEpithet || '';
|
|
220
|
+
if (name.startsWith(species + ' ')) {
|
|
221
|
+
name = name.slice(species.length + 1);
|
|
222
|
+
}
|
|
223
|
+
// Remove rank abbreviations
|
|
224
|
+
name = name.replace(/^(st|r|ab|f|var|ssp|subsp)\. /, '');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (compareRanks('genus', rank) <= 0) {
|
|
228
|
+
// Remove genus
|
|
229
|
+
var genus = parentContext.incorrect.genus || parentContext.genus || '';
|
|
230
|
+
if (name[0] === genus[0] && name.toLowerCase().startsWith(genus.toLowerCase() + ' (')) {
|
|
231
|
+
name = name.slice(genus.length + 1);
|
|
232
|
+
}
|
|
233
|
+
// Remove subgenus parentheses
|
|
234
|
+
name = name.replace(/^\((.*?)\)/, '$1');
|
|
235
|
+
}
|
|
236
|
+
// Hybrids
|
|
237
|
+
if (rank === 'genus' && name.startsWith('x ')) {
|
|
238
|
+
name = HYBRID_SIGN + name.slice(2);
|
|
239
|
+
}
|
|
240
|
+
if (rank === 'species' && /(^| )x /.test(name)) {
|
|
241
|
+
name = name.replace(/(^| )x /, HYBRID_SIGN);
|
|
242
|
+
}
|
|
243
|
+
// Divide the name into the main scientific name (only the epithet for taxa
|
|
244
|
+
// lower than genus), the authorship information, and optionally remarks
|
|
245
|
+
var nameParts = name.match(NAME_PATTERN);
|
|
246
|
+
if (!nameParts) {
|
|
247
|
+
throw new Error("Taxon \"".concat(name, "\" could not be parsed"));
|
|
248
|
+
}
|
|
249
|
+
// To encode old names with spaces (e.g. "Orsillus pini canariensis Lindberg, 1953")
|
|
250
|
+
// underscores are used, which are replaced here. This is also used for undescribed
|
|
251
|
+
// species (e.g. "Leiobunum species A") and intergeneric hybrids (e.g. "×Festulpia
|
|
252
|
+
// Festuca rubra × Vulpia bromoides")
|
|
253
|
+
if (nameParts[1].includes('_')) {
|
|
254
|
+
nameParts[1] = nameParts[1].replace(/_/g, ' ');
|
|
255
|
+
}
|
|
256
|
+
var _ = nameParts[0], taxon = nameParts[1], _d = nameParts[2], citation = _d === void 0 ? '' : _d, notes = nameParts[3];
|
|
257
|
+
item.scientificNameAuthorship = capitalizeAuthors(citation);
|
|
258
|
+
item.taxonRemarks = notes;
|
|
259
|
+
item.taxonRank = rank;
|
|
260
|
+
// @ts-expect-error TS1501: This regular expression flag is only available when targeting 'es6' or later.
|
|
261
|
+
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)) {
|
|
262
|
+
throw new Error("Taxon name contains unexpected characters: \"".concat(taxon, "\""));
|
|
263
|
+
}
|
|
264
|
+
// Validate names and recompose binomial and trinomial names
|
|
265
|
+
if (compareRanks('genus', rank) > 0) {
|
|
266
|
+
item.scientificName = capitalize(taxon);
|
|
267
|
+
if (taxon[0].toUpperCase() !== taxon[0]) {
|
|
268
|
+
throw new Error("Taxon name (".concat(rank, ") should be capitalized: \"").concat(taxon, "\""));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else if (rank === 'genus') {
|
|
272
|
+
item.scientificName = capitalizeGenericName(taxon);
|
|
273
|
+
if (taxon[0].toUpperCase() !== taxon[0] || (taxon[0] === HYBRID_SIGN && taxon[1].toUpperCase() !== taxon[1])) {
|
|
274
|
+
throw new Error("Generic epithet should be capitalized: \"".concat(taxon, "\""));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else if (compareRanks('group', rank) > 0) {
|
|
278
|
+
item.genericName = parentContext.genus;
|
|
279
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
280
|
+
item.scientificName = capitalize(taxon);
|
|
281
|
+
if (taxon[0].toUpperCase() !== taxon[0]) {
|
|
282
|
+
throw new Error("Infrageneric epithet should be capitalized: \"".concat(taxon, "\""));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else if (rank === 'group') {
|
|
286
|
+
item.genericName = parentContext.genus;
|
|
287
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
288
|
+
var specificEpithet = taxon.toLowerCase().replace(/(-group)?$/, '');
|
|
289
|
+
item.scientificName = "".concat(item.genericName, " ").concat(specificEpithet, "-group");
|
|
290
|
+
if (taxon.toLowerCase() !== taxon) {
|
|
291
|
+
console.log(item, taxon);
|
|
292
|
+
throw new Error("Group name should be lowercase: \"".concat(taxon, "\""));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else if (rank === 'subgroup') {
|
|
296
|
+
item.genericName = parentContext.genus;
|
|
297
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
298
|
+
var specificEpithet = taxon.toLowerCase().replace(/(-subgroup)?$/, '');
|
|
299
|
+
item.scientificName = "".concat(item.genericName, " ").concat(specificEpithet, "-subgroup");
|
|
300
|
+
if (taxon.toLowerCase() !== taxon) {
|
|
301
|
+
console.log(item, taxon);
|
|
302
|
+
throw new Error("Subgroup name should be lowercase: \"".concat(taxon, "\""));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (compareRanks('species', rank) > 0) {
|
|
306
|
+
item.genericName = parentContext.genus;
|
|
307
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
308
|
+
var specificEpithet = taxon.toLowerCase();
|
|
309
|
+
item.scientificName = "".concat(item.genericName, " ").concat(specificEpithet);
|
|
310
|
+
if (specificEpithet !== taxon) {
|
|
311
|
+
console.log(item, taxon);
|
|
312
|
+
throw new Error("Subgroup name should be lowercase: \"".concat(taxon, "\""));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else if (rank === 'species') {
|
|
316
|
+
item.genericName = parentContext.genus;
|
|
317
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
318
|
+
if (taxon.toLowerCase() !== taxon && !/^[A-Z][a-z]+ [a-z]+\xD7[A-Z][a-z]+ [a-z]+$/.test(taxon)) {
|
|
319
|
+
console.log(item, taxon);
|
|
320
|
+
throw new Error("Specific epithet should be lowercase: \"".concat(taxon, "\""));
|
|
321
|
+
}
|
|
322
|
+
item.specificEpithet = taxon;
|
|
323
|
+
item.scientificName = "".concat(item.genericName, " ").concat(item.specificEpithet);
|
|
324
|
+
}
|
|
325
|
+
else if (compareRanks('species', rank) < 0) {
|
|
326
|
+
item.genericName = parentContext.genus;
|
|
327
|
+
item.infragenericEpithet = parentContext.subgenus;
|
|
328
|
+
item.specificEpithet = parentContext.specificEpithet;
|
|
329
|
+
item.infraspecificEpithet = taxon.toLowerCase();
|
|
330
|
+
// If possible, names below species should have abbreviations for ranks,
|
|
331
|
+
// like "subsp."
|
|
332
|
+
var nameParts_1 = [
|
|
333
|
+
item.genericName,
|
|
334
|
+
item.specificEpithet,
|
|
335
|
+
item.infraspecificEpithet
|
|
336
|
+
];
|
|
337
|
+
if (item.taxonRank in RANK_LABELS) {
|
|
338
|
+
nameParts_1.splice(2, 0, RANK_LABELS[item.taxonRank]);
|
|
339
|
+
}
|
|
340
|
+
item.scientificName = nameParts_1.join(' ');
|
|
341
|
+
if (item.infraspecificEpithet !== taxon) {
|
|
342
|
+
console.log(item, taxon);
|
|
343
|
+
throw new Error("Infraspecific epithet should be lowercase: \"".concat(taxon, "\""));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Re-add authorship information
|
|
347
|
+
item.scientificNameOnly = item.scientificName;
|
|
348
|
+
if (item.scientificNameAuthorship) {
|
|
349
|
+
item.scientificName += " ".concat(item.scientificNameAuthorship);
|
|
350
|
+
}
|
|
351
|
+
return item;
|
|
352
|
+
}
|