@ca-plant-list/ca-plant-list 0.1.6 → 0.1.8

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.
@@ -7,7 +7,7 @@ class BasePageRenderer {
7
7
  static render( outputDir, Taxa ) {
8
8
 
9
9
  // Copy static files
10
- fs.rmSync( outputDir, { force: true, recursive: true, maxRetries: 2, retryDelay: 400 } );
10
+ fs.rmSync( outputDir, { force: true, recursive: true, maxRetries: 2, retryDelay: 1000 } );
11
11
  // First copy default Jekyll files from package.
12
12
  fs.cpSync( Config.getPackageDir() + "/jekyll", outputDir, { recursive: true } );
13
13
  // Then copy Jekyll files from current dir (which may override default files).
package/lib/files.js CHANGED
@@ -1,17 +1,49 @@
1
1
  import * as fs from "node:fs";
2
+ import { default as unzipper } from "unzipper";
2
3
 
3
4
  class Files {
4
5
 
6
+ static createFileFromStream( fileName, inStream ) {
7
+
8
+ function implementation( fileName, inStream, resolve ) {
9
+ const outStream = fs.createWriteStream( fileName );
10
+ outStream.on( "finish", () => { resolve( true ); } );
11
+ inStream.pipe( outStream );
12
+ }
13
+
14
+ return new Promise( ( resolve ) => { implementation( fileName, inStream, resolve ); } );
15
+ }
16
+
5
17
  static async fetch( url, targetFileName ) {
6
18
  const response = await fetch( url );
7
- const data = await response.text();
8
- fs.writeFileSync( targetFileName, data );
19
+ const data = await response.blob();
20
+ const buffer = await data.arrayBuffer();
21
+ fs.writeFileSync( targetFileName, Buffer.from( buffer ) );
9
22
  }
10
23
 
11
24
  static read( path ) {
12
25
  return fs.readFileSync( path, "utf8" );
13
26
  }
14
27
 
28
+ static write( path, data ) {
29
+ if ( fs.existsSync( path ) ) {
30
+ throw new Error( path + " already exists" );
31
+ }
32
+ fs.writeFileSync( path, data );
33
+ }
34
+
35
+ static async zipFileExtract( zipFilePath, fileNameToUnzip, targetFilePath ) {
36
+
37
+ const zipDir = await unzipper.Open.file( zipFilePath );
38
+ for ( const entry of zipDir.files ) {
39
+ if ( entry.path === fileNameToUnzip ) {
40
+ await this.createFileFromStream( targetFilePath, entry.stream() );
41
+ break;
42
+ }
43
+ }
44
+
45
+ }
46
+
15
47
  }
16
48
 
17
49
  export { Files };
package/lib/html.js CHANGED
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * @deprecated
3
3
  */
4
- const HTML_OPTIONS = {
4
+ export const HTML_OPTIONS = {
5
5
  OPEN_NEW: 1,
6
6
  NO_ESCAPE: 2,
7
7
  };
8
8
 
9
- class HTML {
9
+ /** HTML utility functions. */
10
+ export class HTML {
10
11
 
11
12
  static arrayToLI( items ) {
12
13
  return items.reduce( ( itemHTML, currVal ) => itemHTML + "<li>" + currVal + "</li>", "" );
@@ -77,5 +78,3 @@ class HTML {
77
78
  }
78
79
 
79
80
  }
80
-
81
- export { HTML, HTML_OPTIONS };
package/lib/index.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export class Files {
2
+ static createFileFromStream(fileName: any, inStream: any): Promise<any>;
3
+ static fetch(url: any, targetFileName: any): Promise<void>;
4
+ static read(path: any): string;
5
+ static write(path: any, data: any): void;
6
+ static zipFileExtract(zipFilePath: any, fileNameToUnzip: any, targetFilePath: any): Promise<void>;
7
+ }
8
+ export namespace HTML_OPTIONS {
9
+ const OPEN_NEW: number;
10
+ const NO_ESCAPE: number;
11
+ }
12
+ /** HTML utility functions. */
13
+ export class HTML {
14
+ static arrayToLI(items: any): any;
15
+ static escapeAttribute(value: any): any;
16
+ static escapeText(text: any): any;
17
+ /**
18
+ * @deprecated
19
+ */
20
+ static getElement(elName: any, text: any, attributes?: {}, options?: number): string;
21
+ static "__#2@#getElement"(elName: any, text: any, attributes: any, escape: any): string;
22
+ static getLink(href: any, linkText: any, attributes?: {}, options?: number): string;
23
+ static renderAttribute(n: any, v: any): string;
24
+ static renderAttributes(attributes: any): string;
25
+ static textElement(elName: any, text: any, attributes?: {}): string;
26
+ static wrap(elName: any, text: any, attributes?: {}): string;
27
+ }
@@ -1,12 +1,15 @@
1
1
  import * as fs from "node:fs";
2
- import { HTML, HTML_OPTIONS } from "./html.js";
3
- import { Taxa } from "./taxa.js";
2
+ import { HTML } from "./html.js";
3
+ import { Taxa, COLUMNS } from "./taxa.js";
4
4
  import { HTMLPage } from "./htmlpage.js";
5
5
  import { PageTaxon } from "./pagetaxon.js";
6
6
  import { Config } from "./config.js";
7
7
  import { RarePlants } from "./rareplants.js";
8
8
  import { BasePageRenderer } from "./basepagerenderer.js";
9
9
 
10
+ const RPI_CESA = [ COLUMNS.COL_SPECIES, COLUMNS.COL_COMMON_NAME, COLUMNS.COL_CESA ];
11
+ const RPI_COLUMNS = [ COLUMNS.COL_SPECIES, COLUMNS.COL_COMMON_NAME, COLUMNS.COL_CNPS_RANK ];
12
+
10
13
  class PageRenderer extends BasePageRenderer {
11
14
 
12
15
  static render( outputDir ) {
@@ -24,7 +27,8 @@ class PageRenderer extends BasePageRenderer {
24
27
 
25
28
  static renderLists( outputDir ) {
26
29
 
27
- function getListArray( listInfo, attributes = {} ) {
30
+ function getListArray( listInfo, attributes = {}, columns ) {
31
+
28
32
  const listArray = [];
29
33
  for ( const list of listInfo ) {
30
34
  const taxa = [];
@@ -45,23 +49,25 @@ class PageRenderer extends BasePageRenderer {
45
49
  fs.writeFileSync( outputDir + "/calflora_" + list.filename + ".txt", calfloraTaxa.join( "\n" ) );
46
50
  fs.writeFileSync( outputDir + "/inat_" + list.filename + ".txt", iNatTaxa.join( "\n" ) );
47
51
 
48
- new PageTaxonList().render( outputDir, taxa, list.filename, list.name );
52
+ const cols = columns ? columns : list.columns;
53
+ new PageTaxonList().render( outputDir, taxa, list.filename, list.name, cols );
49
54
 
50
55
  // Check for sublists.
51
- const subListHTML = list.listInfo ? getListArray( list.listInfo, { class: "indent" } ) : "";
56
+ const subListHTML = list.listInfo ? getListArray( list.listInfo, { class: "indent" }, cols ) : "";
52
57
 
53
58
  listArray.push( HTML.getLink( "./" + list.filename + ".html", list.name ) + " (" + taxa.length + ")" + subListHTML );
54
59
  }
60
+
55
61
  return renderList( listArray, attributes );
56
62
  }
57
63
 
58
64
  function renderList( listsHTML, attributes = {} ) {
59
- return HTML.getElement( "ul", HTML.arrayToLI( listsHTML ), attributes, HTML_OPTIONS.NO_ESCAPE );
65
+ return HTML.wrap( "ul", HTML.arrayToLI( listsHTML ), attributes );
60
66
  }
61
67
 
62
68
  function renderSection( title, listsHTML ) {
63
69
  let html = "<div class=\"section\">";
64
- html += HTML.getElement( "h2", title );
70
+ html += HTML.textElement( "h2", title );
65
71
  html += listsHTML;
66
72
  html += "</div>";
67
73
  return html;
@@ -83,6 +89,7 @@ class PageRenderer extends BasePageRenderer {
83
89
  name: "CNPS Ranked Plants",
84
90
  filename: "list_rpi",
85
91
  include: ( t ) => t.getRPIRank() !== undefined,
92
+ columns: RPI_COLUMNS,
86
93
  listInfo: [
87
94
  {
88
95
  name: RarePlants.getRPIRankDescription( "1A" ),
@@ -120,6 +127,7 @@ class PageRenderer extends BasePageRenderer {
120
127
  name: "California Endangered Species Act",
121
128
  filename: "list_cesa",
122
129
  include: ( t ) => t.getCESA() !== undefined,
130
+ columns: RPI_CESA,
123
131
  listInfo: [
124
132
  {
125
133
  name: RarePlants.getCESADescription( "CE" ),
@@ -150,7 +158,7 @@ class PageRenderer extends BasePageRenderer {
150
158
  let html = "<div class=\"wrapper\">";
151
159
  for ( const section of sections ) {
152
160
 
153
- const listHTML = getListArray( section.listInfo );
161
+ const listHTML = getListArray( section.listInfo, section.listInfo.columns );
154
162
 
155
163
  if ( listHTML.length > 0 ) {
156
164
  html += renderSection( section.title, listHTML );
@@ -170,20 +178,20 @@ class PageRenderer extends BasePageRenderer {
170
178
 
171
179
  class PageTaxonList extends HTMLPage {
172
180
 
173
- render( outputDir, taxa, baseName, title ) {
181
+ render( outputDir, taxa, baseName, title, columns ) {
174
182
 
175
183
  let html = this.getFrontMatter( title );
176
184
 
177
- html += HTML.getElement( "h1", title );
185
+ html += HTML.textElement( "h1", title );
178
186
 
179
187
  html += "<div class=\"wrapper\">";
180
188
 
181
189
  html += "<div class=\"section\">";
182
- html += Taxa.getHTMLTable( taxa );
190
+ html += Taxa.getHTMLTable( taxa, columns );
183
191
  html += "</div>";
184
192
 
185
193
  html += "<div class=\"section\">";
186
- html += HTML.getElement( "h2", "Download" );
194
+ html += HTML.textElement( "h2", "Download" );
187
195
  html += "<ul>";
188
196
  html += "<li>" + HTML.getLink( "./calflora_" + baseName + ".txt", "Calflora List" ) + "</li>";
189
197
  html += "<li>" + HTML.getLink( "./inat_" + baseName + ".txt", "iNaturalist List" ) + "</li>";
package/lib/taxa.js CHANGED
@@ -1,29 +1,56 @@
1
1
  import { Taxon } from "./taxon.js";
2
2
  import { ErrorLog } from "./errorlog.js";
3
- import { HTML, HTML_OPTIONS } from "./html.js";
3
+ import { HTML } from "./html.js";
4
4
  import { CSV } from "./csv.js";
5
+ import { RarePlants } from "./rareplants.js";
6
+
7
+ const COLUMNS = {
8
+ COL_CESA: "CESA",
9
+ COL_COMMON_NAME: "Common Name",
10
+ COL_CNPS_RANK: "CNPS Rank",
11
+ COL_SPECIES: "Species",
12
+ };
13
+
14
+ const DEFAULT_COLUMNS = [ COLUMNS.COL_SPECIES, COLUMNS.COL_COMMON_NAME ];
5
15
 
6
16
  class Taxa {
7
17
 
8
18
  static #taxa = {};
9
19
  static #sortedTaxa;
10
20
 
11
- static getHTMLTable( taxa ) {
21
+ static getHTMLTable( taxa, columns = DEFAULT_COLUMNS ) {
22
+
23
+ let includeRPI = true;
24
+
12
25
  let html = "<table><thead>";
13
- html += HTML.getElement( "th", "Species" );
14
- html += HTML.getElement( "th", "Common Name" );
26
+ for ( const column of columns ) {
27
+ html += HTML.textElement( "th", column );
28
+ if ( column === COLUMNS.COL_CNPS_RANK ) {
29
+ // Don't show rarity with species link if CNPS Rank column is included.
30
+ includeRPI = false;
31
+ }
32
+ }
15
33
  html += "</thead>";
16
34
  html += "<tbody>";
17
35
 
18
36
  for ( const taxon of taxa ) {
19
37
  html += "<tr>";
20
- html += HTML.getElement(
21
- "td",
22
- taxon.getHTMLLink(),
23
- undefined,
24
- HTML_OPTIONS.NO_ESCAPE
25
- );
26
- html += HTML.getElement( "td", taxon.getCommonNames().join( ", " ) );
38
+ for ( const column of columns ) {
39
+ switch ( column ) {
40
+ case COLUMNS.COL_CESA:
41
+ html += HTML.textElement( "td", RarePlants.getCESADescription( taxon.getCESA() ) );
42
+ break;
43
+ case COLUMNS.COL_COMMON_NAME:
44
+ html += HTML.textElement( "td", taxon.getCommonNames().join( ", " ) );
45
+ break;
46
+ case COLUMNS.COL_CNPS_RANK:
47
+ html += HTML.wrap( "td", HTML.wrap( "span", taxon.getRPIRankAndThreat(), taxon.getRPIRankAndThreatTooltip( {} ) ) );
48
+ break;
49
+ case COLUMNS.COL_SPECIES:
50
+ html += HTML.wrap( "td", taxon.getHTMLLink( true, includeRPI ) );
51
+ break;
52
+ }
53
+ }
27
54
  html += "</tr>";
28
55
  }
29
56
 
@@ -93,4 +120,4 @@ class Taxa {
93
120
 
94
121
  }
95
122
 
96
- export { Taxa };
123
+ export { Taxa, COLUMNS };
package/lib/taxon.js CHANGED
@@ -100,14 +100,17 @@ class Taxon {
100
100
  return this.#genus;
101
101
  }
102
102
 
103
- getHTMLLink( href = true ) {
103
+ getHTMLLink( href = true, includeRPI = true ) {
104
104
  href = href ? ( "./" + this.getFileName() ) : undefined;
105
- const className = this.isNative() ? ( this.isRare() ? "rare" : "native" ) : "non-native";
105
+ let className = this.isNative() ? "native" : "non-native";
106
+ if ( includeRPI && this.isRare() ) {
107
+ className = "rare";
108
+ }
106
109
  const attributes = { class: className };
107
110
  if ( className === "rare" ) {
108
- attributes[ "title" ] = RarePlants.getRPIRankAndThreatDescriptions( this.getRPIRankAndThreat() ).join( "\n" );
111
+ this.getRPIRankAndThreatTooltip( attributes );
109
112
  }
110
- return HTML.getElement( "span", HTML.getLink( href, this.getName() ), attributes, HTML_OPTIONS.NO_ESCAPE );
113
+ return HTML.wrap( "span", HTML.getLink( href, this.getName() ), attributes );
111
114
  }
112
115
 
113
116
  getINatID() {
@@ -151,6 +154,11 @@ class Taxon {
151
154
  return this.#rankRPI;
152
155
  }
153
156
 
157
+ getRPIRankAndThreatTooltip( attributes ) {
158
+ attributes[ "title" ] = RarePlants.getRPIRankAndThreatDescriptions( this.getRPIRankAndThreat() ).join( "\n" );
159
+ return attributes;
160
+ }
161
+
154
162
  getRPITaxonLink() {
155
163
  const rpiID = this.getRPIID();
156
164
  if ( !rpiID ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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,16 +10,21 @@
10
10
  "homepage": "https://github.com/ca-plants/ca-plant-list",
11
11
  "type": "module",
12
12
  "exports": {
13
- ".": "./lib/index.js"
13
+ ".": "./lib/index.js",
14
+ "./HTML": "./lib/HTML.js"
14
15
  },
16
+ "types": "./lib/index.d.ts",
15
17
  "bin": {
16
18
  "ca-plant-list": "build-site.js"
17
19
  },
18
20
  "dependencies": {
19
21
  "command-line-args": "^5.2.1",
20
- "csv-parse": "^5.3.1"
22
+ "csv-parse": "^5.3.1",
23
+ "unzipper": "^0.10.11"
21
24
  },
22
25
  "devDependencies": {
23
- "eslint": "^8.26.0"
26
+ "@types/node": "^18.11.9",
27
+ "eslint": "^8.26.0",
28
+ "typescript": "^4.9.3"
24
29
  }
25
- }
30
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ // Change this to match your project
3
+ "files": [
4
+ "lib/index.js",
5
+ ],
6
+ "compilerOptions": {
7
+ "allowJs": true,
8
+ "declaration": true,
9
+ "emitDeclarationOnly": true,
10
+ "outDir": "output",
11
+ "module": "es6",
12
+ "newLine": "lf"
13
+ }
14
+ }