@larsgw/formica 0.1.1 → 0.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,53 @@
1
+ ## [0.2.1](https://github.com/identification-resources/formica/compare/v0.2.0...v0.2.1) (2023-08-10)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **resources:** do not validate old versions ([1a8dd2e](https://github.com/identification-resources/formica/commit/1a8dd2e0e373489e287d1fd89dbb7443245c5214))
7
+ * **resources:** fix check for missing leaf taxa ([035f1ad](https://github.com/identification-resources/formica/commit/035f1ad20efa80b388ec3828d4ace4518c7b344e))
8
+
9
+
10
+
11
+ # [0.2.0](https://github.com/identification-resources/formica/compare/v0.1.1...v0.2.0) (2023-08-09)
12
+
13
+
14
+ ### Features
15
+
16
+ * **catalog:** add taxon_scope column ([6a06c37](https://github.com/identification-resources/formica/commit/6a06c37ec0640a8ffbc258ad6a4d81d84d35fee9))
17
+ * **resources:** check for missing leaf taxa ([387c47d](https://github.com/identification-resources/formica/commit/387c47d10bb7f1fb09a68c10dfa869bb924b7633))
18
+
19
+
20
+
21
+ ## [0.1.1](https://github.com/identification-resources/formica/compare/v0.1.0...v0.1.1) (2023-05-10)
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * **processor:** add Node hashbang ([51dee57](https://github.com/identification-resources/formica/commit/51dee57b8547afba449b2b846bd7d65e7927368f))
27
+
28
+
29
+
30
+ # [0.1.0](https://github.com/identification-resources/formica/compare/fed91fd6f350c47bd067d221a4d0e2278a199dae...v0.1.0) (2023-03-01)
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * **resources:** fix typo in taxon name pattern ([8f1b6cf](https://github.com/identification-resources/formica/commit/8f1b6cfa3858edb83c0aee589b84ad760ab815af))
36
+ * **resources:** improve name parsing heuristics ([a40642e](https://github.com/identification-resources/formica/commit/a40642e9bdaeaae363584712e45f218809a5754b))
37
+
38
+
39
+ ### Features
40
+
41
+ * **catalog:** add SDK for catalog data ([fed91fd](https://github.com/identification-resources/formica/commit/fed91fd6f350c47bd067d221a4d0e2278a199dae))
42
+ * **csv:** allow custom delims in output ([b523614](https://github.com/identification-resources/formica/commit/b523614171b0ff96347cb876a08106df413c4032))
43
+ * **processor:** processor for DwC creation ([0e6f21d](https://github.com/identification-resources/formica/commit/0e6f21dfe00a2892348a4ab04fcaf3d6c8324a91))
44
+ * **processor:** processor for DwC indexing ([279773b](https://github.com/identification-resources/formica/commit/279773b085676cf7cb3eb4d81496ad219ac85bce))
45
+ * **resources:** add SDK for resource text files ([0acd36d](https://github.com/identification-resources/formica/commit/0acd36dde04cc1240cb8be97d48812b201b2685a))
46
+ * **resources:** add support for cluster markings ([112315c](https://github.com/identification-resources/formica/commit/112315c7ebd7151c4e2cc77ab20e0cec3f579db6))
47
+ * **resources:** add support for diffs as input ([9751627](https://github.com/identification-resources/formica/commit/9751627a916f47552661596048f1f0d44d89b102))
48
+ * **resources:** add support for hybrids ([e49e96a](https://github.com/identification-resources/formica/commit/e49e96af9c3958bab2a3b0508b1fc8a018b80716))
49
+ * **validate:** add catalog validation script ([4cdf785](https://github.com/identification-resources/formica/commit/4cdf785afb2c77320cd9b5b76483e3464c0f1cff))
50
+ * **validate:** add script to validate resources ([246fb5d](https://github.com/identification-resources/formica/commit/246fb5dd8da03e736884850874cc51a11be9985c))
51
+
52
+
53
+
File without changes
File without changes
File without changes
File without changes
@@ -44,6 +44,7 @@ var Work = /** @class */ (function (_super) {
44
44
  license: { required: false, multiple: true, format: value_1.FORMATS.LICENSE },
45
45
  key_type: { required: true, multiple: true, format: value_1.FORMATS.KEY_TYPE },
46
46
  taxon: { required: true, multiple: true },
47
+ taxon_scope: { required: false, multiple: true },
47
48
  scope: { required: false, multiple: true },
48
49
  region: { required: true, multiple: true },
49
50
  complete: { required: false, multiple: false, format: value_1.FORMATS.COMPLETE },
@@ -43,6 +43,15 @@ var RANKS = [
43
43
  'race',
44
44
  'stirps' // not ICZN
45
45
  ];
46
+ var MAIN_RANKS = [
47
+ 'kingdom',
48
+ 'phylum',
49
+ 'class',
50
+ 'order',
51
+ 'family',
52
+ 'genus',
53
+ 'species'
54
+ ];
46
55
  var DWC_RANKS = [
47
56
  'kingdom',
48
57
  'phylum',
@@ -350,10 +359,7 @@ function parseHeader(header) {
350
359
  }
351
360
  return metadata;
352
361
  }
353
- function parseResource(resource) {
354
- var _a = resource.split(/(\n---\n+)/), header = _a[0], _ = _a[1], rest = _a.slice(2);
355
- var config = parseHeader(header);
356
- var content = rest.join('');
362
+ function validateResource(config, content) {
357
363
  // Check for too much indentation
358
364
  var longerIndent = new RegExp("^( ){".concat(config.levels.length - 1, "}(?! [+=>] ) "), 'm');
359
365
  var longerIndentMatch = content.match(longerIndent);
@@ -362,6 +368,23 @@ function parseResource(resource) {
362
368
  var line = (content.slice(0, offset).match(/\n/g) || []).length + 1;
363
369
  throw new SyntaxError("Too much indentation at ".concat(line, ":0\n").concat(content.slice(offset).split('\n', 1), "\n^"));
364
370
  }
371
+ // Check for missing leaf taxa
372
+ var leafTaxonRank = config.levels.filter(function (rank) { return MAIN_RANKS.includes(rank); }).pop();
373
+ var leafTaxonParentIndent = config.levels.indexOf(leafTaxonRank) - 1;
374
+ if (leafTaxonRank && leafTaxonParentIndent >= 0) {
375
+ var missingLeafTaxa = new RegExp("^((?: ){0,".concat(leafTaxonParentIndent, "})(?![+=> ] ).*\\n(\\1( )+[+=>].*\\n)*(?!\\1 )"), 'm');
376
+ var missingLeafTaxaMatch = content.match(missingLeafTaxa);
377
+ if (missingLeafTaxaMatch !== null) {
378
+ var offset = missingLeafTaxaMatch.index;
379
+ var line = (content.slice(0, offset).match(/\n/g) || []).length + 1;
380
+ throw new SyntaxError("Missing leaf taxon at ".concat(line, ":0\n").concat(content.slice(offset).split('\n', 1), "\n^"));
381
+ }
382
+ }
383
+ }
384
+ function parseResource(resource) {
385
+ var _a = resource.split(/(\n---\n+)/), header = _a[0], _ = _a[1], rest = _a.slice(2);
386
+ var config = parseHeader(header);
387
+ var content = rest.join('');
365
388
  return [config, content];
366
389
  }
367
390
  function parseResourceContent(content, resource, oldIds) {
@@ -468,6 +491,7 @@ function parseFile(file, id, old) {
468
491
  var oldResources = old ? splitResources(old.txt) : [];
469
492
  return splitResources(file).map(function (resource, index) {
470
493
  var _a = parseResource(resource), config = _a[0], content = _a[1];
494
+ validateResource(config, content);
471
495
  var template = {
472
496
  id: "".concat(id, ":").concat(index + 1),
473
497
  file: "".concat(id, "-").concat(index + 1),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larsgw/formica",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "SDK and tools for data from the Library of Identification Resources",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -11,9 +11,12 @@
11
11
  "loir-resources-index": "./lib/bin/process-resources-index.js"
12
12
  },
13
13
  "scripts": {
14
+ "test": "node --test --test-reporter spec",
14
15
  "lint": "eslint src",
15
16
  "build": "tsc -d",
17
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
16
18
  "preversion": "npm run lint",
19
+ "version": "npm run changelog && git add CHANGELOG.md",
17
20
  "prepublishOnly": "npm run build"
18
21
  },
19
22
  "repository": {
@@ -36,6 +39,7 @@
36
39
  "@types/node": "^18.14.1",
37
40
  "@typescript-eslint/eslint-plugin": "^5.54.0",
38
41
  "@typescript-eslint/parser": "^5.54.0",
42
+ "conventional-changelog-cli": "^3.0.0",
39
43
  "eslint": "^8.35.0",
40
44
  "typescript": "^4.9.5"
41
45
  }
@@ -26,6 +26,7 @@ export class Work extends Entity {
26
26
  license: { required: false, multiple: true, format: FORMATS.LICENSE },
27
27
  key_type: { required: true, multiple: true, format: FORMATS.KEY_TYPE },
28
28
  taxon: { required: true, multiple: true },
29
+ taxon_scope: { required: false, multiple: true },
29
30
  scope: { required: false, multiple: true },
30
31
  region: { required: true, multiple: true },
31
32
  complete: { required: false, multiple: false, format: FORMATS.COMPLETE },
@@ -31,6 +31,16 @@ const RANKS: Rank[] = [
31
31
  'stirps' // not ICZN
32
32
  ]
33
33
 
34
+ const MAIN_RANKS: Rank[] = [
35
+ 'kingdom',
36
+ 'phylum',
37
+ 'class',
38
+ 'order',
39
+ 'family',
40
+ 'genus',
41
+ 'species'
42
+ ]
43
+
34
44
  const DWC_RANKS: DwcRank[] = [
35
45
  'kingdom',
36
46
  'phylum',
@@ -360,11 +370,7 @@ function parseHeader (header: string): ResourceMetadata {
360
370
  return metadata
361
371
  }
362
372
 
363
- function parseResource (resource: string): [ResourceMetadata, string] {
364
- const [header, _, ...rest] = resource.split(/(\n---\n+)/)
365
- const config = parseHeader(header)
366
- const content = rest.join('')
367
-
373
+ function validateResource (config: ResourceMetadata, content: string) {
368
374
  // Check for too much indentation
369
375
  const longerIndent = new RegExp(`^( ){${config.levels.length - 1}}(?! [+=>] ) `, 'm')
370
376
  const longerIndentMatch = content.match(longerIndent)
@@ -376,6 +382,27 @@ ${content.slice(offset).split('\n', 1)}
376
382
  ^`)
377
383
  }
378
384
 
385
+ // Check for missing leaf taxa
386
+ const leafTaxonRank = config.levels.filter(rank => MAIN_RANKS.includes(rank)).pop() as string
387
+ const leafTaxonParentIndent = config.levels.indexOf(leafTaxonRank) - 1
388
+ if (leafTaxonRank && leafTaxonParentIndent >= 0) {
389
+ const missingLeafTaxa = new RegExp(`^((?: ){0,${leafTaxonParentIndent}})(?![+=> ] ).*\\n(\\1( )+[+=>].*\\n)*(?!\\1 )`, 'm')
390
+ const missingLeafTaxaMatch = content.match(missingLeafTaxa)
391
+ if (missingLeafTaxaMatch !== null) {
392
+ const offset = missingLeafTaxaMatch.index
393
+ const line = (content.slice(0, offset).match(/\n/g) || []).length + 1
394
+ throw new SyntaxError(`Missing leaf taxon at ${line}:0
395
+ ${content.slice(offset).split('\n', 1)}
396
+ ^`)
397
+ }
398
+ }
399
+ }
400
+
401
+ function parseResource (resource: string): [ResourceMetadata, string] {
402
+ const [header, _, ...rest] = resource.split(/(\n---\n+)/)
403
+ const config = parseHeader(header)
404
+ const content = rest.join('')
405
+
379
406
  return [config, content]
380
407
  }
381
408
 
@@ -488,6 +515,7 @@ export function parseFile (file: string, id: WorkId, old?: ResourceHistory): Res
488
515
  const oldResources = old ? splitResources(old.txt) : []
489
516
  return splitResources(file).map((resource, index) => {
490
517
  const [config, content] = parseResource(resource)
518
+ validateResource(config, content)
491
519
  const template: Resource = {
492
520
  id: `${id}:${index + 1}`,
493
521
  file: `${id}-${index + 1}`,
@@ -0,0 +1,19 @@
1
+ const test = require('node:test')
2
+ const assert = require('assert')
3
+
4
+ const { resources } = require('../lib')
5
+
6
+ test('resources', async (t) => {
7
+ await t.test('missing leaf taxa', (t) => {
8
+ assert.throws(() => {
9
+ resources.parseTextFile(`---
10
+ levels: [family, genus, species]
11
+ ---
12
+
13
+ Cydnidae
14
+ Cydnidae
15
+ Legnotus
16
+ limbosus`, 'T1')
17
+ })
18
+ })
19
+ })
package/lib/types.d.ts DELETED
@@ -1,62 +0,0 @@
1
- type Value = string[] | string;
2
- type SingleValue = string;
3
- interface FieldError {
4
- field: string;
5
- error: string;
6
- }
7
- interface WorkError extends FieldError {
8
- entity: WorkId;
9
- }
10
- type Rank = string;
11
- type DwcRank = Rank;
12
- type TaxonStatus = string;
13
- type TaxonId = string;
14
- type ResourceId = string;
15
- type WorkId = string;
16
- interface WorkingTaxon {
17
- scientificNameID?: TaxonId;
18
- scientificName?: string;
19
- scientificNameAuthorship?: string;
20
- genericName?: string;
21
- infragenericEpithet?: string;
22
- specificEpithet?: string;
23
- intraspecificEpithet?: string;
24
- taxonRank?: Rank;
25
- taxonRemarks?: string;
26
- collectionCode?: ResourceId;
27
- taxonomicStatus?: TaxonStatus;
28
- acceptedNameUsageID?: TaxonId;
29
- acceptedNameUsage?: string;
30
- parentNameUsageID?: TaxonId;
31
- parentNameUsage?: string;
32
- kingdom?: string;
33
- phylum?: string;
34
- class?: string;
35
- order?: string;
36
- family?: string;
37
- subfamily?: string;
38
- genus?: string;
39
- subgenus?: string;
40
- higherClassification?: string;
41
- scientificNameOnly?: string;
42
- incorrect?: WorkingTaxon;
43
- }
44
- interface Taxon extends WorkingTaxon {
45
- scientificNameID: TaxonId;
46
- scientificName: string;
47
- taxonRank: Rank;
48
- collectionCode: ResourceId;
49
- taxonomicStatus: string;
50
- }
51
- interface ResourceMetadata {
52
- levels: Rank[];
53
- scope: string[];
54
- catalog?: object;
55
- }
56
- interface Resource {
57
- id: string;
58
- file: string;
59
- workId: string;
60
- metadata: ResourceMetadata;
61
- taxa: Record<TaxonId, Taxon>;
62
- }
package/lib/types.js DELETED
File without changes