@ca-plant-list/ca-plant-list 0.4.37 → 0.4.39

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/lib/csv.js CHANGED
@@ -10,6 +10,7 @@ export class CSV {
10
10
  * @param {string} fileName
11
11
  * @param {import("csv-parse").ColumnOption[]|boolean|function (string[]):string[]} columns
12
12
  * @param {string|undefined} delimiter
13
+ * @returns {import("csv-parse").Options}
13
14
  */
14
15
  static #getOptions(fileName, columns, delimiter) {
15
16
  /** @type {import("csv-parse").Options} */
@@ -104,6 +105,8 @@ export class CSV {
104
105
  static readFile(fileName, columns = true, delimiter) {
105
106
  const content = fs.readFileSync(fileName);
106
107
  const options = this.#getOptions(fileName, columns, delimiter);
108
+ /** @type {T[]} */
109
+ // @ts-ignore - need to get options @type to have correct column options
107
110
  return parseSync(content, options);
108
111
  }
109
112
 
@@ -126,6 +129,8 @@ export class CSV {
126
129
  const content = fs.readFileSync(fileName);
127
130
  const options = this.#getOptions(fileName, getHeaders, delimiter);
128
131
 
132
+ /** @type {T[]} */
133
+ // @ts-ignore - need to get options @type to have correct column options
129
134
  const data = parseSync(content, options);
130
135
  if (headers === undefined) {
131
136
  throw new Error();
@@ -16,9 +16,9 @@ export class EBookPage {
16
16
  this.#rootPrefix = rootPrefix;
17
17
  }
18
18
 
19
- create() {
19
+ async create() {
20
20
  let html = this.#renderPageStart(this.#title);
21
- html += this.renderPageBody();
21
+ html += await this.renderPageBody();
22
22
  html += this.#renderPageEnd();
23
23
  fs.writeFileSync(this.#fileName, html);
24
24
  }
@@ -31,7 +31,10 @@ export class EBookPage {
31
31
  return "<body>";
32
32
  }
33
33
 
34
- renderPageBody() {
34
+ /**
35
+ * @returns {Promise<string>}
36
+ */
37
+ async renderPageBody() {
35
38
  throw new Error("must be implemented by subclass");
36
39
  }
37
40
 
@@ -1,7 +1,7 @@
1
1
  import { EBookPage } from "../ebookpage.js";
2
2
  import { XHTML } from "../xhtml.js";
3
3
 
4
- class PageListFamilies extends EBookPage {
4
+ export class PageListFamilies extends EBookPage {
5
5
  #families;
6
6
 
7
7
  /**
@@ -13,7 +13,7 @@ class PageListFamilies extends EBookPage {
13
13
  this.#families = families;
14
14
  }
15
15
 
16
- renderPageBody() {
16
+ async renderPageBody() {
17
17
  const html = XHTML.textElement("h1", this.getTitle());
18
18
 
19
19
  const links = [];
@@ -27,5 +27,3 @@ class PageListFamilies extends EBookPage {
27
27
  return html + XHTML.wrap("ol", XHTML.arrayToLI(links));
28
28
  }
29
29
  }
30
-
31
- export { PageListFamilies };
@@ -5,7 +5,7 @@ import { EBook } from "../ebook.js";
5
5
 
6
6
  const FN_FLOWER_TIME_INDEX = "fm.html";
7
7
 
8
- class PageListFlowers {
8
+ export class PageListFlowers {
9
9
  /**
10
10
  * @param {string} contentDir
11
11
  * @param {import("../../types.js").Taxa} taxa
@@ -70,7 +70,7 @@ class PageListFlowerTimeIndex extends EBookPage {
70
70
  super(outputDir + "/" + FN_FLOWER_TIME_INDEX, "Flowering Times");
71
71
  }
72
72
 
73
- renderPageBody() {
73
+ async renderPageBody() {
74
74
  const html = XHTML.textElement("h1", this.getTitle());
75
75
  return html + PageListFlowers.renderMonthLinks();
76
76
  }
@@ -106,7 +106,7 @@ class PageListFlowerTime extends EBookPage {
106
106
  return "list_fm_" + m1 + ".html";
107
107
  }
108
108
 
109
- renderPageBody() {
109
+ async renderPageBody() {
110
110
  const html = XHTML.textElement("h1", this.getTitle());
111
111
 
112
112
  /** @type {[number,number]} */
@@ -123,5 +123,3 @@ class PageListFlowerTime extends EBookPage {
123
123
  return html + XHTML.wrap("ol", XHTML.arrayToLI(links));
124
124
  }
125
125
  }
126
-
127
- export { PageListFlowers, PageListFlowerTime };
@@ -1,7 +1,7 @@
1
1
  import { EBookPage } from "../ebookpage.js";
2
2
  import { XHTML } from "../xhtml.js";
3
3
 
4
- class PageListFlowerColor extends EBookPage {
4
+ export class PageListFlowerColor extends EBookPage {
5
5
  #color;
6
6
 
7
7
  /**
@@ -16,7 +16,7 @@ class PageListFlowerColor extends EBookPage {
16
16
  this.#color = color;
17
17
  }
18
18
 
19
- renderPageBody() {
19
+ async renderPageBody() {
20
20
  const html = XHTML.textElement("h1", this.getTitle());
21
21
 
22
22
  const links = [];
@@ -27,5 +27,3 @@ class PageListFlowerColor extends EBookPage {
27
27
  return html + XHTML.wrap("ol", XHTML.arrayToLI(links));
28
28
  }
29
29
  }
30
-
31
- export { PageListFlowerColor };
@@ -2,7 +2,7 @@ import { HTMLTaxon } from "../../htmltaxon.js";
2
2
  import { EBookPage } from "../ebookpage.js";
3
3
  import { XHTML } from "../xhtml.js";
4
4
 
5
- class PageListSpecies extends EBookPage {
5
+ export class PageListSpecies extends EBookPage {
6
6
  #taxa;
7
7
 
8
8
  /**
@@ -17,7 +17,7 @@ class PageListSpecies extends EBookPage {
17
17
  this.#taxa = taxa;
18
18
  }
19
19
 
20
- renderPageBody() {
20
+ async renderPageBody() {
21
21
  const html = XHTML.textElement("h1", this.getTitle());
22
22
 
23
23
  const links = [];
@@ -28,5 +28,3 @@ class PageListSpecies extends EBookPage {
28
28
  return html + XHTML.wrap("ol", XHTML.arrayToLI(links));
29
29
  }
30
30
  }
31
-
32
- export { PageListSpecies };
@@ -1,3 +1,4 @@
1
+ import { imageSizeFromFile } from "image-size/fromFile";
1
2
  import { EBookPage } from "../ebookpage.js";
2
3
  import { XHTML } from "../xhtml.js";
3
4
  import { Markdown } from "../../markdown.js";
@@ -5,7 +6,6 @@ import { HTMLTaxon } from "../../htmltaxon.js";
5
6
  import { Config } from "../../config.js";
6
7
  import { Files } from "../../files.js";
7
8
  import { Images } from "../images.js";
8
- import imageSize from "image-size";
9
9
 
10
10
  class TaxonPage extends EBookPage {
11
11
  #config;
@@ -25,7 +25,7 @@ class TaxonPage extends EBookPage {
25
25
  this.#images = images;
26
26
  }
27
27
 
28
- renderPageBody() {
28
+ async renderPageBody() {
29
29
  /**
30
30
  * @param {string} name
31
31
  */
@@ -72,7 +72,7 @@ class TaxonPage extends EBookPage {
72
72
 
73
73
  let photoHTML = "";
74
74
  for (const photo of photos) {
75
- const dimensions = imageSize.imageSize(
75
+ const dimensions = await imageSizeFromFile(
76
76
  this.#images.getCompressedFilePath(photo),
77
77
  );
78
78
  let img = XHTML.textElement("img", "", {
@@ -14,7 +14,7 @@ class TOCPage extends EBookPage {
14
14
  this.#taxa = taxa;
15
15
  }
16
16
 
17
- renderPageBody() {
17
+ async renderPageBody() {
18
18
  let html = '<nav id="toc" role="doc-toc" epub:type="toc">';
19
19
  html += '<h1 epub:type="title">Table of Contents</h1>';
20
20
 
@@ -56,12 +56,13 @@ class PlantBook extends EBook {
56
56
 
57
57
  for (let index = 0; index < taxonList.length; index++) {
58
58
  const taxon = taxonList[index];
59
- new TaxonPage(
59
+ const page = new TaxonPage(
60
60
  contentDir,
61
61
  this.#config,
62
62
  taxon,
63
63
  this.#images,
64
- ).create();
64
+ );
65
+ await page.create();
65
66
  meter.update(index + 1);
66
67
  }
67
68
  meter.stop();
@@ -226,7 +226,7 @@ class Taxon extends Taxonomy {
226
226
 
227
227
  getINatName() {
228
228
  const name = this.#iNatSyn ? this.#iNatSyn : this.getName();
229
- return name.replace(/ (subsp|var)\./, "").replace("×", "× ");
229
+ return name.replace(/ (cf|subsp|var)\./, "").replace("×", "× ");
230
230
  }
231
231
 
232
232
  /**
package/lib/tools/rpi.js CHANGED
@@ -4,7 +4,7 @@ import { Files } from "../files.js";
4
4
  import { TaxaCSV } from "./taxacsv.js";
5
5
 
6
6
  /**
7
- * @typedef {"CESA"|"CNDDB"|"FESA"|"Global"} Rank
7
+ * @typedef {"CRPR"|"CESA"|"CNDDB"|"FESA"|"Global"} Rank
8
8
  * @typedef {Map<string,Map<Rank,string|undefined>>} RanksToUpdate
9
9
  */
10
10
 
@@ -166,17 +166,13 @@ export class RPI {
166
166
  }
167
167
  }
168
168
 
169
- if (rank !== taxon.getRPIRankAndThreat()) {
170
- if (taxon.isNative()) {
171
- errorLog.log(
172
- name,
173
- "rank in taxa.csv is different than rank in " +
174
- fileName,
175
- taxon.getRPIRankAndThreat(),
176
- rank,
177
- );
178
- }
179
- }
169
+ checkStatusMatch(
170
+ name,
171
+ "CRPR",
172
+ rank,
173
+ taxon.getRPIRankAndThreat(),
174
+ ranksToUpdate,
175
+ );
180
176
  checkStatusMatch(
181
177
  name,
182
178
  "CESA",
@@ -249,7 +245,7 @@ export class RPI {
249
245
 
250
246
  this.#checkExceptions(taxa, config, exceptions, errorLog);
251
247
 
252
- this.#scrape(toolsDataDir, taxa, exceptions, errorLog);
248
+ this.#scrape(toolsDataDir, dataDir, taxa, exceptions, errorLog, update);
253
249
 
254
250
  if (update) {
255
251
  this.#updateRanks(dataDir, ranksToUpdate);
@@ -421,11 +417,20 @@ export class RPI {
421
417
 
422
418
  /**
423
419
  * @param {string} toolsDataDir
420
+ * @param {string} dataDir
424
421
  * @param {import("../types.js").Taxa} taxa
425
422
  * @param {import("../exceptions.js").Exceptions} exceptions
426
423
  * @param {import("../errorlog.js").ErrorLog} errorLog
424
+ * @param {boolean} update
427
425
  */
428
- static async #scrape(toolsDataDir, taxa, exceptions, errorLog) {
426
+ static async #scrape(
427
+ toolsDataDir,
428
+ dataDir,
429
+ taxa,
430
+ exceptions,
431
+ errorLog,
432
+ update,
433
+ ) {
429
434
  const toolsDataPath = toolsDataDir + "/rpi";
430
435
  const fileName = HTML_FILE_NAME;
431
436
  const filePath = toolsDataPath + "/" + fileName;
@@ -444,6 +449,9 @@ export class RPI {
444
449
  rpiIDs[name] = id;
445
450
  }
446
451
 
452
+ /** @type {Map<string,string>} */
453
+ const idsToUpdate = new Map();
454
+
447
455
  for (const taxon of taxa.getTaxonList()) {
448
456
  if (!taxon.getRPIRankAndThreat()) {
449
457
  continue;
@@ -463,8 +471,13 @@ export class RPI {
463
471
  id,
464
472
  taxon.getRPIID(),
465
473
  );
474
+ idsToUpdate.set(name, id);
466
475
  }
467
476
  }
477
+
478
+ if (update) {
479
+ this.#updateIDs(dataDir, idsToUpdate);
480
+ }
468
481
  }
469
482
 
470
483
  /**
@@ -485,6 +498,24 @@ export class RPI {
485
498
  return false;
486
499
  }
487
500
 
501
+ /**
502
+ * @param {string} dataDir
503
+ * @param {Map<string,string>} idsToUpdate
504
+ */
505
+ static #updateIDs(dataDir, idsToUpdate) {
506
+ const taxa = new TaxaCSV(dataDir);
507
+
508
+ for (const taxonData of taxa.getTaxa()) {
509
+ const id = idsToUpdate.get(taxonData.taxon_name);
510
+ if (!id) {
511
+ continue;
512
+ }
513
+ taxonData["RPI ID"] = id;
514
+ }
515
+
516
+ taxa.write();
517
+ }
518
+
488
519
  /**
489
520
  * @param {string} dataDir
490
521
  * @param {RanksToUpdate} ranksToUpdate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.4.37",
3
+ "version": "0.4.39",
4
4
  "description": "Tools to create files for a website listing plants in an area of California.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,18 +36,18 @@
36
36
  "inatobsphotos": "scripts/inatobsphotos.js"
37
37
  },
38
38
  "dependencies": {
39
- "@11ty/eleventy": "^3.0.0",
39
+ "@11ty/eleventy": "^3.1.2",
40
40
  "@htmltools/scrape": "^0.1.1",
41
- "archiver": "^5.3.1",
41
+ "archiver": "^7.0.1",
42
42
  "cli-progress": "^3.12.0",
43
- "commander": "^14.0.1",
44
- "csv-parse": "^5.6.0",
45
- "csv-stringify": "^6.5.2",
43
+ "commander": "^14.0.2",
44
+ "csv-parse": "^6.1.0",
45
+ "csv-stringify": "^6.6.0",
46
46
  "exceljs": "^4.4.0",
47
- "image-size": "^1.1.1",
47
+ "image-size": "^2.0.2",
48
48
  "markdown-it": "^14.1.0",
49
- "sharp": "^0.33.5",
50
- "svgo-ll": "^6.0.1",
49
+ "sharp": "^0.34.4",
50
+ "svgo-ll": "^6.2.0",
51
51
  "unzipper": "^0.12.3"
52
52
  },
53
53
  "devDependencies": {
@@ -57,10 +57,10 @@
57
57
  "@types/markdown-it": "^14.1.2",
58
58
  "@types/node": "^22.10.7",
59
59
  "@types/unzipper": "^0.10.9",
60
- "eslint": "^9.35.0",
61
- "jest": "^29.7.0",
60
+ "eslint": "^9.39.0",
61
+ "jest": "^30.2.0",
62
62
  "prettier": "^3.6.2",
63
- "puppeteer": "^24.1.1",
64
- "typescript": "^5.9.2"
63
+ "puppeteer": "^24.27.0",
64
+ "typescript": "^5.9.3"
65
65
  }
66
66
  }