@ca-plant-list/ca-plant-list 0.3.2 → 0.3.4

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.
Files changed (70) hide show
  1. package/.vscode/settings.json +4 -2
  2. package/data/exceptions.json +0 -3
  3. package/data/glossary/calyx.md +1 -0
  4. package/data/glossary/ovary.md +3 -0
  5. package/data/glossary/pedicel.md +1 -0
  6. package/data/glossary/pistil.md +3 -0
  7. package/data/glossary/sepal.md +1 -0
  8. package/data/glossary/stigma.md +3 -0
  9. package/data/glossary/style.md +3 -0
  10. package/data/illustrations/inkscape/pistil.svg +156 -0
  11. package/data/synonyms.csv +8 -3
  12. package/data/taxa.csv +77 -72
  13. package/data/text/Calochortus-argillosus.md +1 -0
  14. package/data/text/Calochortus-luteus.md +1 -0
  15. package/data/text/Calochortus-venustus.md +1 -0
  16. package/data/text/Ceanothus-cuneatus-var-cuneatus.md +1 -0
  17. package/data/text/Ceanothus-leucodermis.md +1 -0
  18. package/data/text/Claytonia-parviflora.md +1 -0
  19. package/data/text/Claytonia-perfoliata.md +1 -0
  20. package/data/text/Collinsia-heterophylla-var-heterophylla.md +1 -0
  21. package/data/text/Delphinium-californicum-subsp-californicum.md +1 -0
  22. package/data/text/Delphinium-decorum-subsp-decorum.md +1 -0
  23. package/data/text/Delphinium-hesperium-subsp-hesperium.md +1 -0
  24. package/data/text/Delphinium-patens-subsp-patens.md +1 -0
  25. package/data/text/Galium-andrewsii-subsp-gatense.md +1 -0
  26. package/data/text/Helianthella-californica-var-californica.md +1 -0
  27. package/data/text/Helianthella-castanea.md +1 -0
  28. package/data/text/Iris-macrosiphon.md +1 -0
  29. package/data/text/Lasthenia-californica-subsp-californica.md +1 -0
  30. package/data/text/Lasthenia-gracilis.md +1 -0
  31. package/data/text/Leptosiphon-ambiguus.md +1 -0
  32. package/data/text/Leptosiphon-androsaceus.md +1 -0
  33. package/data/text/Leptosiphon-bicolor.md +1 -1
  34. package/data/text/Leptosiphon-parviflorus.md +1 -0
  35. package/data/text/Lithophragma-affine.md +1 -1
  36. package/data/text/Lithophragma-parviflorum-var-parviflorum.md +1 -1
  37. package/data/text/Lomatium-californicum.md +0 -0
  38. package/data/text/Lomatium-dasycarpum-subsp-dasycarpum.md +1 -0
  39. package/data/text/Lomatium-utriculatum.md +1 -0
  40. package/data/text/Nemophila-heterophylla.md +1 -0
  41. package/data/text/Nemophila-parviflora-var-parviflora.md +1 -0
  42. package/data/text/Plectritis-ciliosa.md +1 -0
  43. package/data/text/Plectritis-macrocera.md +1 -0
  44. package/data/text/Primula-clevelandii-var-patula.md +1 -0
  45. package/data/text/Primula-hendersonii.md +1 -0
  46. package/data/text/Sidalcea-diploscypha.md +1 -0
  47. package/data/text/Thysanocarpus-curvipes.md +1 -0
  48. package/data/text/Thysanocarpus-laciniatus.md +1 -0
  49. package/data/text/Viola-douglasii.md +1 -0
  50. package/data/text/Viola-pedunculata.md +1 -0
  51. package/data/text/Viola-purpurea-subsp-quercetorum.md +1 -0
  52. package/ebook/css/main.css +5 -0
  53. package/lib/basepagerenderer.js +30 -29
  54. package/lib/dateutils.js +36 -16
  55. package/lib/ebook/pages/page_list_families.js +15 -9
  56. package/lib/ebook/pages/taxonpage.js +63 -36
  57. package/lib/ebook/plantbook.js +82 -48
  58. package/lib/errorlog.js +16 -11
  59. package/lib/families.js +109 -74
  60. package/lib/files.js +103 -45
  61. package/lib/genera.js +40 -26
  62. package/lib/generictaxaloader.js +15 -7
  63. package/lib/index.js +38 -3
  64. package/lib/taxa.js +174 -87
  65. package/lib/taxaloader.js +28 -15
  66. package/lib/taxaprocessor.js +6 -8
  67. package/lib/taxon.js +115 -57
  68. package/package.json +4 -6
  69. package/scripts/build-ebook.js +33 -25
  70. package/lib/index.d.ts +0 -345
package/lib/taxon.js CHANGED
@@ -1,6 +1,7 @@
1
- import { Genera } from "./genera.js";
2
- import { HTML } from "./html.js";
1
+ import { HTML } from "./index.js";
3
2
  import { RarePlants } from "./rareplants.js";
3
+ // eslint-disable-next-line no-unused-vars
4
+ import { Genera } from "./genera.js";
4
5
 
5
6
  const TAXA_COLNAMES = {
6
7
  BLOOM_START: "bloom_start",
@@ -10,15 +11,18 @@ const TAXA_COLNAMES = {
10
11
  };
11
12
 
12
13
  class Taxon {
13
-
14
+ /** @type {Genera} */
15
+ #genera;
14
16
  #name;
15
17
  #genus;
16
18
  #commonNames;
17
19
  #status;
18
20
  #jepsonID;
19
21
  #calRecNum;
22
+ /**@type {string|undefined} */
20
23
  #cfSyn;
21
24
  #iNatID;
25
+ /**@type {string|undefined} */
22
26
  #iNatSyn;
23
27
  #flowerColors;
24
28
  #bloomStart;
@@ -29,38 +33,54 @@ class Taxon {
29
33
  #fesa;
30
34
  #rankCNDDB;
31
35
  #rankGlobal;
36
+ /** @type {string[]} */
32
37
  #synonyms = [];
33
38
 
34
- constructor( data ) {
35
- const name = data[ "taxon_name" ];
36
- const commonNames = data[ TAXA_COLNAMES.COMMON_NAME ];
37
- const cesa = data[ "CESA" ];
38
- const fesa = data[ "FESA" ];
39
- const rankGlobal = data[ "GRank" ];
40
- const rankCNDDB = data[ "SRank" ];
39
+ /**
40
+ * @param {import("./index.js").TaxonData} data
41
+ * @param {Genera} genera
42
+ */
43
+ constructor(data, genera) {
44
+ this.#genera = genera;
45
+ const name = data["taxon_name"];
46
+ const commonNames = data["common name"];
47
+ const cesa = data["CESA"];
48
+ const fesa = data["FESA"];
49
+ const rankGlobal = data["GRank"];
50
+ const rankCNDDB = data["SRank"];
41
51
  this.#name = name;
42
- this.#genus = name.split( " " )[ 0 ];
43
- this.#commonNames = commonNames ? commonNames.split( "," ).map( t => t.trim() ) : [];
44
- this.#status = data[ "status" ];
45
- this.#jepsonID = data[ "jepson id" ];
46
- this.#calRecNum = data[ "calrecnum" ];
47
- this.#iNatID = data[ "inat id" ];
48
- const colors = data[ TAXA_COLNAMES.FLOWER_COLOR ];
49
- this.#flowerColors = colors ? colors.split( "," ) : undefined;
50
- this.#bloomStart = data[ TAXA_COLNAMES.BLOOM_START ];
51
- this.#bloomEnd = data[ TAXA_COLNAMES.BLOOM_END ];
52
- this.#rpiID = data[ "RPI ID" ];
53
- this.#rankRPI = data[ "CRPR" ];
52
+ this.#genus = name.split(" ")[0];
53
+ this.#commonNames = commonNames
54
+ ? commonNames.split(",").map((t) => t.trim())
55
+ : [];
56
+ this.#status = data["status"];
57
+ this.#jepsonID = data["jepson id"];
58
+ this.#calRecNum = data["calrecnum"];
59
+ this.#iNatID = data["inat id"];
60
+ const colors = data["flower_color"];
61
+ this.#flowerColors = colors ? colors.split(",") : undefined;
62
+ if (data["bloom_start"]) {
63
+ this.#bloomStart = parseInt(data["bloom_start"]);
64
+ }
65
+ if (data["bloom_end"]) {
66
+ this.#bloomEnd = parseInt(data["bloom_end"]);
67
+ }
68
+ this.#rpiID = data["RPI ID"];
69
+ this.#rankRPI = data["CRPR"];
54
70
  this.#cesa = cesa ? cesa : undefined;
55
71
  this.#fesa = fesa ? fesa : undefined;
56
72
  this.#rankCNDDB = rankCNDDB ? rankCNDDB : undefined;
57
73
  this.#rankGlobal = rankGlobal ? rankGlobal : undefined;
58
- Genera.addTaxon( this );
74
+ genera.addTaxon(this);
59
75
  }
60
76
 
61
- addSynonym( syn, type ) {
62
- this.#synonyms.push( syn );
63
- switch ( type ) {
77
+ /**
78
+ * @param {string} syn
79
+ * @param {string} type
80
+ */
81
+ addSynonym(syn, type) {
82
+ this.#synonyms.push(syn);
83
+ switch (type) {
64
84
  case "CF":
65
85
  // Synonym is in Calflora format.
66
86
  this.#cfSyn = syn;
@@ -74,22 +94,28 @@ class Taxon {
74
94
 
75
95
  getBaseFileName() {
76
96
  // Convert spaces to "-" and remove ".".
77
- return this.#name.replaceAll( " ", "-" ).replaceAll( ".", "" );
97
+ return this.#name.replaceAll(" ", "-").replaceAll(".", "");
78
98
  }
79
99
 
100
+ /**
101
+ * @returns {number|undefined}
102
+ */
80
103
  getBloomEnd() {
81
104
  return this.#bloomEnd;
82
105
  }
83
106
 
107
+ /**
108
+ * @returns {number|undefined}
109
+ */
84
110
  getBloomStart() {
85
111
  return this.#bloomStart;
86
112
  }
87
113
 
88
114
  getCalfloraName() {
89
- if ( this.#cfSyn ) {
115
+ if (this.#cfSyn) {
90
116
  return this.#cfSyn;
91
117
  }
92
- return this.getName().replace( " subsp.", " ssp." ).replace( "×", "X" );
118
+ return this.getName().replace(" subsp.", " ssp.").replace("×", "X");
93
119
  }
94
120
 
95
121
  getCalfloraID() {
@@ -98,11 +124,16 @@ class Taxon {
98
124
 
99
125
  getCalfloraTaxonLink() {
100
126
  const calfloraID = this.getCalfloraID();
101
- if ( !calfloraID ) {
127
+ if (!calfloraID) {
102
128
  return;
103
129
  }
104
- const link = HTML.getLink( "https://www.calflora.org/app/taxon?crn=" + calfloraID, "Calflora", {}, true );
105
- return this.#cfSyn ? ( link + " (" + this.#cfSyn + ")" ) : link;
130
+ const link = HTML.getLink(
131
+ "https://www.calflora.org/app/taxon?crn=" + calfloraID,
132
+ "Calflora",
133
+ {},
134
+ true
135
+ );
136
+ return this.#cfSyn ? link + " (" + this.#cfSyn + ")" : link;
106
137
  }
107
138
 
108
139
  getCESA() {
@@ -118,14 +149,14 @@ class Taxon {
118
149
  }
119
150
 
120
151
  getFamily() {
121
- return Genera.getFamily( this.#genus );
152
+ return this.#genera.getFamily(this.#genus);
122
153
  }
123
154
 
124
155
  getFESA() {
125
156
  return this.#fesa;
126
157
  }
127
158
 
128
- getFileName( ext = "html" ) {
159
+ getFileName(ext = "html") {
129
160
  return this.getBaseFileName() + "." + ext;
130
161
  }
131
162
 
@@ -134,7 +165,7 @@ class Taxon {
134
165
  }
135
166
 
136
167
  getGenus() {
137
- return Genera.getGenus( this.#genus );
168
+ return this.#genera.getGenus(this.#genus);
138
169
  }
139
170
 
140
171
  getGenusName() {
@@ -145,18 +176,29 @@ class Taxon {
145
176
  return this.#rankGlobal;
146
177
  }
147
178
 
148
- getHTMLLink( href = true, includeRPI = true ) {
149
- href = href ? ( "./" + this.getFileName() ) : undefined;
179
+ /**
180
+ *
181
+ * @param {boolean|string|undefined} href
182
+ * @param {boolean} includeRPI
183
+ */
184
+ getHTMLLink(href = true, includeRPI = true) {
185
+ href = href ? "./" + this.getFileName() : undefined;
150
186
  let className = this.isNative() ? "native" : "non-native";
151
187
  let isRare = false;
152
- if ( includeRPI && this.isRare() ) {
188
+ if (includeRPI && this.isRare()) {
153
189
  isRare = true;
154
190
  className += " rare";
155
191
  }
156
192
  const attributes = { class: className };
157
- const link = HTML.wrap( "span", HTML.getLink( href, this.getName() ), attributes );
158
- if ( isRare ) {
159
- return HTML.getToolTip( link, this.getRPIRankAndThreatTooltip(), { icon: false } );
193
+ const link = HTML.wrap(
194
+ "span",
195
+ HTML.getLink(href, this.getName()),
196
+ attributes
197
+ );
198
+ if (isRare) {
199
+ return HTML.getToolTip(link, this.getRPIRankAndThreatTooltip(), {
200
+ icon: false,
201
+ });
160
202
  }
161
203
  return link;
162
204
  }
@@ -167,7 +209,7 @@ class Taxon {
167
209
 
168
210
  getINatName() {
169
211
  const name = this.#iNatSyn ? this.#iNatSyn : this.getName();
170
- return name.replace( / (subsp|var)\./, "" ).replace( "×", "× " );
212
+ return name.replace(/ (subsp|var)\./, "").replace("×", "× ");
171
213
  }
172
214
 
173
215
  getINatSyn() {
@@ -176,11 +218,16 @@ class Taxon {
176
218
 
177
219
  getINatTaxonLink() {
178
220
  const iNatID = this.getINatID();
179
- if ( !iNatID ) {
221
+ if (!iNatID) {
180
222
  return "";
181
223
  }
182
- const link = HTML.getLink( "https://www.inaturalist.org/taxa/" + iNatID, "iNaturalist", {}, true );
183
- return this.#iNatSyn ? ( link + " (" + this.#iNatSyn + ")" ) : link;
224
+ const link = HTML.getLink(
225
+ "https://www.inaturalist.org/taxa/" + iNatID,
226
+ "iNaturalist",
227
+ {},
228
+ true
229
+ );
230
+ return this.#iNatSyn ? link + " (" + this.#iNatSyn + ")" : link;
184
231
  }
185
232
 
186
233
  getJepsonID() {
@@ -196,10 +243,10 @@ class Taxon {
196
243
  }
197
244
 
198
245
  getRPIRank() {
199
- if ( !this.#rankRPI ) {
246
+ if (!this.#rankRPI) {
200
247
  return this.#rankRPI;
201
248
  }
202
- return this.#rankRPI.split( "." )[ 0 ];
249
+ return this.#rankRPI.split(".")[0];
203
250
  }
204
251
 
205
252
  getRPIRankAndThreat() {
@@ -207,15 +254,22 @@ class Taxon {
207
254
  }
208
255
 
209
256
  getRPIRankAndThreatTooltip() {
210
- return RarePlants.getRPIRankAndThreatDescriptions( this.getRPIRankAndThreat() ).join( "<br>" );
257
+ return RarePlants.getRPIRankAndThreatDescriptions(
258
+ this.getRPIRankAndThreat()
259
+ ).join("<br>");
211
260
  }
212
261
 
213
262
  getRPITaxonLink() {
214
263
  const rpiID = this.getRPIID();
215
- if ( !rpiID ) {
264
+ if (!rpiID) {
216
265
  return "";
217
266
  }
218
- const link = HTML.getLink( "https://rareplants.cnps.org/Plants/Details/" + rpiID, "CNPS Rare Plant Inventory", {}, true );
267
+ const link = HTML.getLink(
268
+ "https://rareplants.cnps.org/Plants/Details/" + rpiID,
269
+ "CNPS Rare Plant Inventory",
270
+ {},
271
+ true
272
+ );
219
273
  return link;
220
274
  }
221
275
 
@@ -223,16 +277,20 @@ class Taxon {
223
277
  return this.#status;
224
278
  }
225
279
 
226
- getStatusDescription( config ) {
227
- switch ( this.#status ) {
280
+ /**
281
+ * @param {*} config
282
+ * @returns
283
+ */
284
+ getStatusDescription(config) {
285
+ switch (this.#status) {
228
286
  case "N":
229
287
  return "Native";
230
288
  case "NC":
231
- return config.getLabel( "status-NC", "Introduced" );
289
+ return config.getLabel("status-NC", "Introduced");
232
290
  case "X":
233
291
  return "Introduced";
234
292
  }
235
- throw new Error( this.#status );
293
+ throw new Error(this.#status);
236
294
  }
237
295
 
238
296
  getSynonyms() {
@@ -257,7 +315,7 @@ class Taxon {
257
315
 
258
316
  shouldHaveFlowers() {
259
317
  const sectionName = this.getFamily().getSectionName();
260
- switch ( sectionName ) {
318
+ switch (sectionName) {
261
319
  case "Ceratophyllales":
262
320
  case "Eudicots":
263
321
  case "Magnoliids":
@@ -269,9 +327,9 @@ class Taxon {
269
327
  case "Lycophytes":
270
328
  return false;
271
329
  default:
272
- throw new Error( sectionName );
330
+ throw new Error(sectionName);
273
331
  }
274
332
  }
275
333
  }
276
334
 
277
- export { TAXA_COLNAMES, Taxon };
335
+ export { TAXA_COLNAMES, Taxon };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Tools to create Jekyll files for a website listing plants in an area of California.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -10,11 +10,7 @@
10
10
  "homepage": "https://github.com/ca-plants/ca-plant-list",
11
11
  "type": "module",
12
12
  "exports": {
13
- ".": "./lib/index.js",
14
- "./Families": "./lib/families.js",
15
- "./Files": "./lib/files.js",
16
- "./HTML": "./lib/html.js",
17
- "./Jekyll": "./lib/jekyll.js"
13
+ ".": "./lib/index.js"
18
14
  },
19
15
  "types": "./lib/index.d.ts",
20
16
  "bin": {
@@ -35,6 +31,8 @@
35
31
  },
36
32
  "devDependencies": {
37
33
  "@types/node": "^18.11.9",
34
+ "@types/unzipper": "^0.10.9",
35
+ "dts-bundle-generator": "^9.3.1",
38
36
  "eslint": "^8.26.0",
39
37
  "typescript": "^5.3.3"
40
38
  }
@@ -6,64 +6,72 @@ import { Files } from "../lib/files.js";
6
6
  import { TaxaProcessor } from "../lib/taxaprocessor.js";
7
7
  import { CommandProcessor } from "../lib/commandprocessor.js";
8
8
 
9
- const OPTION_DEFS = [
10
- { name: "locationsdir", type: String },
11
- ];
9
+ const OPTION_DEFS = [{ name: "locationsdir", type: String }];
12
10
 
13
11
  const OPTION_HELP = [
14
12
  {
15
13
  name: "locationsdir",
16
14
  type: String,
17
15
  typeLabel: "{underline path}",
18
- description: "If this option is specified, multiple ebooks will be generated. {bold locationsdir} must be a subdirectory"
19
- + " of the current directory, and each subdirectory of {bold locationsdir} is processed in turn to generate an ebook."
20
- + " Each ebook is placed in a subdirectory of {bold outputdir}."
16
+ description:
17
+ "If this option is specified, multiple ebooks will be generated. {bold locationsdir} must be a subdirectory" +
18
+ " of the current directory, and each subdirectory of {bold locationsdir} is processed in turn to generate an ebook." +
19
+ " Each ebook is placed in a subdirectory of {bold outputdir}.",
21
20
  },
22
21
  ];
23
22
 
24
23
  class BookCommand extends CommandProcessor {
25
-
26
24
  constructor() {
27
- super( "ca-plant-book", "A tool to generate an ebook with local plant data.", OPTION_DEFS, OPTION_HELP );
25
+ super(
26
+ "ca-plant-book",
27
+ "A tool to generate an ebook with local plant data.",
28
+ OPTION_DEFS,
29
+ OPTION_HELP
30
+ );
28
31
  }
29
-
30
32
  }
31
33
 
32
- async function commandRunner( tp ) {
34
+ /**
35
+ * @param {TaxaProcessor} tp
36
+ */
37
+ async function commandRunner(tp) {
33
38
  const options = tp.getOptions();
34
- const ebook = new PlantBook( options.outputdir, new Config( options.datadir ), tp.getTaxa() );
39
+ const ebook = new PlantBook(
40
+ options.outputdir,
41
+ new Config(options.datadir),
42
+ tp.getTaxa()
43
+ );
35
44
  await ebook.create();
36
45
  }
37
46
 
38
- async function generateEBooks( options ) {
47
+ async function generateEBooks(options) {
39
48
  const locationsDir = options.locationsdir;
40
49
 
41
50
  // If there is a "locations" directory, generate a book for all subdirectories.
42
- if ( locationsDir ) {
51
+ if (locationsDir) {
43
52
  // Generate ebook for each location.
44
53
  const outputBase = options.outputdir;
45
- const subdirs = Files.getDirEntries( locationsDir );
46
- for ( const subdir of subdirs ) {
47
- console.log( "Generating " + subdir );
54
+ const subdirs = Files.getDirEntries(locationsDir);
55
+ for (const subdir of subdirs) {
56
+ console.log("Generating " + subdir);
48
57
  const suffix = "/" + subdir;
49
58
  const path = locationsDir + suffix;
50
- if ( Files.isDir( path ) ) {
59
+ if (Files.isDir(path)) {
51
60
  options.datadir = path;
52
61
  options.outputdir = outputBase + suffix;
53
- const gen = new TaxaProcessor( options );
54
- await gen.process( commandRunner );
62
+ const gen = new TaxaProcessor(options);
63
+ await gen.process(commandRunner);
55
64
  }
56
65
  }
57
66
  } else {
58
67
  // Otherwise use the default directory.
59
- const gen = new TaxaProcessor( options );
60
- await gen.process( commandRunner );
68
+ const gen = new TaxaProcessor(options);
69
+ await gen.process(commandRunner);
61
70
  }
62
-
63
71
  }
64
72
 
65
73
  const cmd = new BookCommand();
66
74
  const options = cmd.getOptions();
67
- if ( !options.help ) {
68
- generateEBooks( options );
69
- }
75
+ if (!options.help) {
76
+ await generateEBooks(options);
77
+ }