@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/test/resources.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const test = require('node:test')
|
|
1
|
+
const { suite, test } = require('node:test')
|
|
2
2
|
const assert = require('assert')
|
|
3
3
|
|
|
4
4
|
const { catalog, resources } = require('../lib')
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
await
|
|
6
|
+
suite('catalog', async (t) => {
|
|
7
|
+
await test('reports missing required fields', (t) => {
|
|
8
8
|
const errors = catalog.loadData(`id
|
|
9
9
|
B1`, 'catalog').validate()
|
|
10
10
|
assert.deepStrictEqual(errors, [
|
|
@@ -18,8 +18,8 @@ B1`, 'catalog').validate()
|
|
|
18
18
|
})
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
await
|
|
21
|
+
suite('resources', async (t) => {
|
|
22
|
+
await test('parses author with initials', (t) => {
|
|
23
23
|
const [resource] = resources.parseTextFile(`---
|
|
24
24
|
levels: [family, genus, species]
|
|
25
25
|
---
|
|
@@ -33,7 +33,7 @@ Sphecidae A. Costa, 1886
|
|
|
33
33
|
assert.deepStrictEqual(Object.values(resource.taxa).map(taxon => taxon.scientificNameAuthorship), Array(3).fill('A. Costa, 1886'))
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
await
|
|
36
|
+
await test('does not validate name with correction', (t) => {
|
|
37
37
|
const [resource] = resources.parseTextFile(`---
|
|
38
38
|
levels: [species]
|
|
39
39
|
---
|
|
@@ -45,7 +45,7 @@ Clytochrysus lapidarius (Panzer, 1804)
|
|
|
45
45
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Clytochrysus lapidarius (Panzer, 1804)')
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
await
|
|
48
|
+
await test('errors for missing leaf taxa', (t) => {
|
|
49
49
|
assert.throws(() => {
|
|
50
50
|
resources.parseTextFile(`---
|
|
51
51
|
levels: [family, genus, species]
|
|
@@ -58,7 +58,7 @@ Cydnidae
|
|
|
58
58
|
})
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
await
|
|
61
|
+
await test('parses synonyms starting with intraspecific ranks', (t) => {
|
|
62
62
|
const [resource] = resources.parseTextFile(`---
|
|
63
63
|
levels: [species]
|
|
64
64
|
---
|
|
@@ -69,7 +69,7 @@ Lygaeus equestris (Linnaeus, 1758)
|
|
|
69
69
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Lygaeus equestris f. lactans Horváth, 1899')
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
await
|
|
72
|
+
await test('parses accepted taxa starting with intraspecific ranks', (t) => {
|
|
73
73
|
const [resource] = resources.parseTextFile(`---
|
|
74
74
|
levels: [species, form]
|
|
75
75
|
---
|
|
@@ -80,7 +80,7 @@ Lygaeus equestris (Linnaeus, 1758)
|
|
|
80
80
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Lygaeus equestris f. lactans Horváth, 1899')
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
await
|
|
83
|
+
await test('does not validate "indet." lines', (t) => {
|
|
84
84
|
const [resource] = resources.parseTextFile(`---
|
|
85
85
|
levels: [genus, species]
|
|
86
86
|
---
|
|
@@ -91,7 +91,7 @@ Drymus
|
|
|
91
91
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Drymus')
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
await
|
|
94
|
+
await test('parses names containing non-ASCII characters', (t) => {
|
|
95
95
|
const [resource] = resources.parseTextFile(`---
|
|
96
96
|
levels: [species]
|
|
97
97
|
---
|
|
@@ -101,7 +101,7 @@ Nematus fåhraei Thomson
|
|
|
101
101
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Nematus fåhraei Thomson')
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
await
|
|
104
|
+
await test('parses synonyms in different genera', (t) => {
|
|
105
105
|
const [resource] = resources.parseTextFile(`---
|
|
106
106
|
levels: [species]
|
|
107
107
|
---
|
|
@@ -113,7 +113,7 @@ Katamenes arbustorum subsp. burlinii
|
|
|
113
113
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Eumenes arbustorum var. burlinii')
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
await
|
|
116
|
+
await test('parses species without generic names', (t) => {
|
|
117
117
|
const [resource] = resources.parseTextFile(`---
|
|
118
118
|
levels: [genus, subgenus, species]
|
|
119
119
|
---
|
|
@@ -125,7 +125,7 @@ Microdynerus Thomson, 1874
|
|
|
125
125
|
assert.strictEqual(resource.taxa['T1:1:3'].scientificName, 'Microdynerus microdynerus (Dalla Torre, 1889)')
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
await
|
|
128
|
+
await test('parses hybrids', (t) => {
|
|
129
129
|
const [resource] = resources.parseTextFile(`---
|
|
130
130
|
levels: [species]
|
|
131
131
|
---
|
|
@@ -137,7 +137,7 @@ Tilia x vulgaris
|
|
|
137
137
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Tilia ×vulgaris')
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
await
|
|
140
|
+
await test('outputs corrected generic names', (t) => {
|
|
141
141
|
const [resource] = resources.parseTextFile(`---
|
|
142
142
|
levels: [genus, species]
|
|
143
143
|
---
|
|
@@ -150,7 +150,7 @@ Bogdania Kerzhner, 1964
|
|
|
150
150
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Bogdiana myrmica Kerzhner, 1964')
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
-
await
|
|
153
|
+
await test('parses cross-genus hybrids', (t) => {
|
|
154
154
|
const [resource] = resources.parseTextFile(`---
|
|
155
155
|
levels: [genus, species]
|
|
156
156
|
---
|
|
@@ -165,7 +165,7 @@ x Festulpia
|
|
|
165
165
|
assert.strictEqual(resource.taxa['T1:1:3'].scientificName, '×Festulpia Festuca rubra×Vulpia bromoides')
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
await
|
|
168
|
+
await test('parses cross-genus hybrids without parent context', (t) => {
|
|
169
169
|
const [resource] = resources.parseTextFile(`---
|
|
170
170
|
levels: [species]
|
|
171
171
|
---
|
|
@@ -175,4 +175,86 @@ x Festulpia Festuca_rubra x Vulpia_bromoides
|
|
|
175
175
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, '×Festulpia Festuca rubra×Vulpia bromoides')
|
|
176
176
|
assert.strictEqual(resource.taxa['T1:1:1'].verbatimIdentification, '× Festulpia Festuca rubra × Vulpia bromoides')
|
|
177
177
|
})
|
|
178
|
+
|
|
179
|
+
await test('handles skips in ranks', (t) => {
|
|
180
|
+
const [resource] = resources.parseTextFile(`---
|
|
181
|
+
levels: [family, genus, species]
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
Apidae
|
|
185
|
+
Apis mellifera
|
|
186
|
+
`, 'T1')
|
|
187
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Apis mellifera')
|
|
188
|
+
assert.strictEqual(resource.taxa['T1:1:2'].genus, 'Apis')
|
|
189
|
+
assert.strictEqual(resource.taxa['T1:1:2'].taxonRank, 'species')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
await test('synonyms do not break recognition of missing leaf taxa (1)', (t) => {
|
|
193
|
+
const [resource] = resources.parseTextFile(`---
|
|
194
|
+
levels: [genus, subgenus, species]
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
Bombus
|
|
198
|
+
= Psithyrus
|
|
199
|
+
Bombus
|
|
200
|
+
pascuorum
|
|
201
|
+
`, 'T1')
|
|
202
|
+
assert.strictEqual(resource.taxa['T1:1:4'].scientificName, 'Bombus pascuorum')
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
await test('synonyms do not break recognition of missing leaf taxa (2)', (t) => {
|
|
206
|
+
assert.throws(() => {
|
|
207
|
+
resources.parseTextFile(`---
|
|
208
|
+
levels: [genus, subgenus, species]
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
Bombus
|
|
212
|
+
Psithyrus
|
|
213
|
+
= Psithirus
|
|
214
|
+
Bombus
|
|
215
|
+
pascuorum
|
|
216
|
+
`, 'T1')
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
await test('synonyms do not break recognition of missing leaf taxa (3)', (t) => {
|
|
221
|
+
assert.throws(() => {
|
|
222
|
+
resources.parseTextFile(`---
|
|
223
|
+
levels: [family, genus, species]
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
Acanthosomidae
|
|
227
|
+
= Acanthosomatidae
|
|
228
|
+
Cydnidae
|
|
229
|
+
Legnotus
|
|
230
|
+
limbosus
|
|
231
|
+
`, 'T1')
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
await test('parses genera with subgenus-rank synonyms', (t) => {
|
|
236
|
+
const [resource] = resources.parseTextFile(`---
|
|
237
|
+
levels: [genus]
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
Ectemnius Dahlbom
|
|
241
|
+
= Crabro (Ectemnius) Dahlbom
|
|
242
|
+
`, 'T1')
|
|
243
|
+
assert.strictEqual(resource.taxa['T1:1:2'].taxonRank, 'subgenus')
|
|
244
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Ectemnius Dahlbom')
|
|
245
|
+
assert.strictEqual(resource.taxa['T1:1:2'].genericName, 'Crabro')
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
await test('parses genera with like-name subgenera', (t) => {
|
|
249
|
+
const [resource] = resources.parseTextFile(`---
|
|
250
|
+
levels: [genus, subgenus]
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
Polistes Latreille, 1802
|
|
254
|
+
Polistes Latreille, 1802
|
|
255
|
+
`, 'T1')
|
|
256
|
+
assert.strictEqual(resource.taxa['T1:1:2'].taxonRank, 'subgenus')
|
|
257
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Polistes Latreille, 1802')
|
|
258
|
+
assert.strictEqual(resource.taxa['T1:1:2'].genericName, 'Polistes')
|
|
259
|
+
})
|
|
178
260
|
})
|