@ca-plant-list/ca-plant-list 0.3.7 → 0.4.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.
Files changed (75) hide show
  1. package/.vscode/settings.json +7 -1
  2. package/data/synonyms.csv +95 -2
  3. package/data/taxa.csv +53 -22
  4. package/data/text/Antennaria-media.md +1 -0
  5. package/data/text/Antennaria-rosea-subsp-rosea.md +1 -0
  6. package/data/text/Camassia-leichtlinii-subsp-suksdorfii.md +1 -0
  7. package/data/text/Camassia-quamash-subsp-breviflora.md +1 -0
  8. package/data/text/Delphinium-glaucum.md +1 -0
  9. package/data/text/Delphinium-nuttallianum.md +1 -0
  10. package/data/text/Drymocallis-glandulosa-var-glandulosa.md +1 -0
  11. package/data/text/Drymocallis-lactea-var-austiniae.md +1 -0
  12. package/data/text/Erigeron-compositus.md +1 -0
  13. package/data/text/Erigeron-glacialis-var-glacialis.md +1 -0
  14. package/data/text/Eriogonum-incanum.md +1 -0
  15. package/data/text/Eriogonum-lobbii.md +1 -0
  16. package/data/text/Eriogonum-ovalifolium-var-nivale.md +1 -0
  17. package/data/text/Erythranthe-breweri.md +1 -0
  18. package/data/text/Erythranthe-erubescens.md +1 -0
  19. package/data/text/Navarretia-leptalea-subsp-bicolor.md +1 -0
  20. package/data/text/Navarretia-leptalea-subsp-leptalea.md +1 -0
  21. package/data/text/Polemonium-californicum.md +1 -0
  22. package/data/text/Polemonium-pulcherrimum-var-pulcherrimum.md +1 -0
  23. package/data/text/Primula-jeffreyi.md +1 -0
  24. package/data/text/Primula-tetrandra.md +1 -0
  25. package/data/text/Silene-douglasii-var-douglasii.md +1 -0
  26. package/data/text/Silene-lemmonii.md +1 -0
  27. package/data/text/Silene-sargentii.md +1 -0
  28. package/lib/basepagerenderer.js +3 -4
  29. package/lib/config.js +42 -19
  30. package/lib/csv.js +54 -36
  31. package/lib/ebook/ebook.js +84 -57
  32. package/lib/ebook/ebookpage.js +22 -11
  33. package/lib/ebook/ebooksitegenerator.js +36 -14
  34. package/lib/ebook/glossarypages.js +20 -17
  35. package/lib/ebook/images.js +13 -5
  36. package/lib/ebook/pages/page_list_families.js +0 -2
  37. package/lib/ebook/pages/page_list_flower_color.js +14 -9
  38. package/lib/ebook/pages/page_list_flowers.js +59 -41
  39. package/lib/ebook/pages/taxonpage.js +1 -1
  40. package/lib/ebook/pages/tocpage.js +26 -20
  41. package/lib/ebook/plantbook.js +6 -13
  42. package/lib/ebook/{image.js → taxonimage.js} +2 -2
  43. package/lib/exceptions.js +42 -26
  44. package/lib/externalsites.js +11 -4
  45. package/lib/families.js +10 -8
  46. package/lib/flowercolor.js +42 -0
  47. package/lib/genera.js +13 -21
  48. package/lib/genericpage.js +12 -0
  49. package/lib/html.js +11 -23
  50. package/lib/htmltaxon.js +89 -6
  51. package/lib/index.d.ts +54 -0
  52. package/lib/index.js +2 -30
  53. package/lib/jekyll.js +49 -21
  54. package/lib/jepson.js +7 -8
  55. package/lib/markdown.js +6 -0
  56. package/lib/pagerenderer.js +43 -8
  57. package/lib/plants/glossary.js +5 -0
  58. package/lib/program.js +47 -0
  59. package/lib/rareplants.js +44 -30
  60. package/lib/sitegenerator.js +41 -24
  61. package/lib/taxa.js +20 -134
  62. package/lib/taxon.js +1 -1
  63. package/lib/web/glossarypages.js +6 -0
  64. package/lib/web/pagetaxon.js +1 -5
  65. package/package.json +8 -7
  66. package/schemas/exceptions.schema.json +57 -0
  67. package/scripts/build-ebook.js +38 -47
  68. package/scripts/build-site.js +25 -14
  69. package/types/classes.d.ts +136 -8
  70. package/lib/commandandtaxaprocessor.js +0 -25
  71. package/lib/commandprocessor.js +0 -108
  72. package/lib/generictaxaloader.js +0 -48
  73. package/lib/taxaloader.js +0 -50
  74. package/lib/taxaprocessor.js +0 -34
  75. /package/data/text/{Calyptridium-ubellatum.md → Calyptridium-umbellatum.md} +0 -0
@@ -0,0 +1 @@
1
+ Brown or black phyllaries.
@@ -0,0 +1 @@
1
+ Pink or white phyllaries.
@@ -0,0 +1 @@
1
+ Petals and sepals indistinguishable, usually with 5 veins, 20-40 mm long.
@@ -0,0 +1 @@
1
+ Petals and sepals indistinguishable, usually with 3 veins, 10-20 mm long.
@@ -0,0 +1 @@
1
+ Tall, often to 1.5 meters or occasionally taller. Lower part of stem leafless and coated with whitish powder.
@@ -0,0 +1 @@
1
+ Plant usually 50 cm or less in height.
@@ -0,0 +1 @@
1
+ Sepals at least as long as petals.
@@ -0,0 +1 @@
1
+ Petals longer than sepals.
@@ -0,0 +1 @@
1
+ Leaves mostly basal and usually dissected into 3 parts; may have small undissected leaves along stem. Basal leaves with dense short hairs. May or may not have ray flowers.
@@ -0,0 +1 @@
1
+ Phyllary hairs glandular, phyllaries roughly equal in length. Leaves not dissected, generally 5-20 cm long, and much longer than they are wide.
@@ -0,0 +1 @@
1
+ Leaves fuzzy.
@@ -0,0 +1 @@
1
+ Leaves round. flower stems prostrate.
@@ -0,0 +1 @@
1
+ Leaves small and densely packed.
@@ -0,0 +1 @@
1
+ Small flowers (tube throat 4-8 mm).
@@ -0,0 +1 @@
1
+ Large flowers (tube throat 20-50 mm). Flowers light pink with dark purple stripes. Plant hairy.
@@ -0,0 +1 @@
1
+ Corolla throat yellow, sometimes with purple marks. Corolla throat 2-5 mm long.
@@ -0,0 +1 @@
1
+ Corolla throat purple, 6-8 mm long.
@@ -0,0 +1 @@
1
+ Terminal leaflet fused with adjacent leaflets. Preferred habitat is montane forest.
@@ -0,0 +1 @@
1
+ Terminal leaflet not fused with adjacent leaflets. Preferred habitat is rock talus.
@@ -0,0 +1 @@
1
+ Stem hairy and glandular.
@@ -0,0 +1 @@
1
+ Stem not hairy.
@@ -0,0 +1 @@
1
+ Petals with two lobes.
@@ -0,0 +1 @@
1
+ Flowers nodding. Petals with 4 lobes. Stamens and styles protruding significantly beyond petals.
@@ -0,0 +1 @@
1
+ Petals with 4 lobes; outer lobes pointed, smaller than inner lobes.
@@ -1,16 +1,15 @@
1
1
  import { Config } from "./config.js";
2
2
  import { Files } from "./files.js";
3
3
  import { Jekyll } from "./jekyll.js";
4
- import { Taxa } from "./taxa.js";
5
4
  import { GlossaryPages } from "./web/glossarypages.js";
6
5
 
7
6
  class BasePageRenderer {
8
7
  /**
9
8
  * @param {string} outputDir
10
9
  * @param {Taxa} taxa
11
- * @param {*} familyCols
10
+ * @param {TaxaCol[]} [familyCols]
12
11
  */
13
- static render(outputDir, taxa, familyCols) {
12
+ static renderBasePages(outputDir, taxa, familyCols) {
14
13
  const siteGenerator = new Jekyll(outputDir);
15
14
 
16
15
  // Copy static files
@@ -20,7 +19,7 @@ class BasePageRenderer {
20
19
  Files.copyDir("jekyll", outputDir);
21
20
 
22
21
  // Copy illustrations.
23
- siteGenerator.copyIllustrations(Taxa.getFlowerColors());
22
+ siteGenerator.copyIllustrations(taxa.getFlowerColors());
24
23
 
25
24
  taxa.getFamilies().renderPages(outputDir, familyCols);
26
25
 
package/lib/config.js CHANGED
@@ -3,46 +3,69 @@ import * as url from "node:url";
3
3
  import { Files } from "./files.js";
4
4
 
5
5
  class Config {
6
+ static #packageDir = path.dirname(
7
+ path.dirname(url.fileURLToPath(import.meta.url))
8
+ );
6
9
 
7
- static #packageDir = path.dirname( path.dirname( url.fileURLToPath( import.meta.url ) ) );
8
-
10
+ /** @type {Object<string,Object<string,Object<string,{}>>>} */
9
11
  #config = {};
10
12
 
11
- constructor( dataDir ) {
12
- this.#config = JSON.parse( Files.read( dataDir + "/config.json" ) );
13
+ /**
14
+ * @param {string} dataDir
15
+ */
16
+ constructor(dataDir) {
17
+ this.#config = JSON.parse(Files.read(dataDir + "/config.json"));
13
18
  }
14
19
 
15
- getConfigValue( prefix, name, subcat, dflt ) {
16
- const obj = this.#config[ prefix ];
17
- if ( obj ) {
18
- if ( Object.hasOwn( obj, name ) ) {
19
- const nameObj = obj[ name ];
20
- if ( nameObj === undefined ) {
20
+ /**
21
+ * @param {string} prefix
22
+ * @param {string} name
23
+ * @param {string|undefined} subcat
24
+ * @param {string} dflt
25
+ * @returns {string}
26
+ */
27
+ getConfigValue(prefix, name, subcat, dflt) {
28
+ const obj = this.#config[prefix];
29
+ if (obj) {
30
+ if (Object.hasOwn(obj, name)) {
31
+ const nameObj = obj[name];
32
+ if (nameObj === undefined) {
21
33
  return dflt;
22
34
  }
23
- if ( subcat === undefined ) {
24
- return nameObj;
35
+ if (subcat === undefined) {
36
+ return typeof nameObj === "string" ? nameObj : dflt;
25
37
  }
26
- if ( Object.hasOwn( nameObj, subcat ) ) {
27
- return nameObj[ subcat ];
38
+ if (Object.hasOwn(nameObj, subcat)) {
39
+ const v = nameObj[subcat];
40
+ return typeof v === "string" ? v : dflt;
28
41
  }
29
42
  }
30
43
  }
31
44
  return dflt;
32
45
  }
33
46
 
47
+ /**
48
+ * @returns {string[]}
49
+ */
34
50
  getCountyCodes() {
35
- return this.#config[ "counties" ];
51
+ const counties = this.#config["counties"];
52
+ if (counties instanceof Array) {
53
+ return counties;
54
+ }
55
+ return [];
36
56
  }
37
57
 
38
- getLabel( name, dflt ) {
39
- return this.getConfigValue( "labels", name, undefined, dflt );
58
+ /**
59
+ * @param {string} name
60
+ * @param {string} dflt
61
+ */
62
+ getLabel(name, dflt) {
63
+ return this.getConfigValue("labels", name, undefined, dflt);
40
64
  }
41
65
 
42
66
  static getPackageDir() {
43
67
  return this.#packageDir;
44
68
  }
45
-
46
69
  }
47
70
 
48
- export { Config };
71
+ export { Config };
package/lib/csv.js CHANGED
@@ -5,81 +5,99 @@ import { parse as parseSync } from "csv-parse/sync";
5
5
  import { parse } from "csv-parse";
6
6
 
7
7
  class CSV {
8
+ /**
9
+ * @param {string} dir
10
+ * @param {string} fileName
11
+ */
12
+ static getMap(dir, fileName) {
13
+ /** @type {string[]} */
14
+ let headers = [];
8
15
 
9
- static getMap( dir, fileName ) {
10
-
11
- let headers;
12
-
13
- function setHeaders( h ) {
16
+ /**
17
+ * @param {string[]} h
18
+ */
19
+ function setHeaders(h) {
14
20
  headers = h;
15
21
  return h;
16
22
  }
17
23
 
18
- const csv = this.parseFile( dir, fileName, setHeaders );
24
+ const csv = this.parseFile(dir, fileName, setHeaders);
25
+ /** @type {Object<string,string[]>} */
19
26
  const map = {};
20
- for ( const row of csv ) {
21
- map[ row[ headers[ 0 ] ] ] = row[ headers[ 1 ] ];
27
+ for (const row of csv) {
28
+ map[row[headers[0]]] = row[headers[1]];
22
29
  }
23
30
 
24
31
  return map;
25
32
  }
26
33
 
27
- static #getOptions( fileName, columns, delimiter ) {
34
+ /**
35
+ * @param {string} fileName
36
+ * @param {import("csv-parse").ColumnOption[]|boolean|function (string[]):string[]} columns
37
+ * @param {string|undefined} delimiter
38
+ */
39
+ static #getOptions(fileName, columns, delimiter) {
40
+ /** @type {import("csv-parse").Options} */
28
41
  const options = { relax_column_count_less: true };
29
42
  options.columns = columns;
30
- if ( path.extname( fileName ) === ".tsv" ) {
43
+ if (path.extname(fileName) === ".tsv") {
31
44
  options.delimiter = "\t";
32
45
  options.quote = false;
33
46
  } else {
34
47
  options.delimiter = delimiter ? delimiter : ",";
35
48
  }
36
- if ( options.delimiter === "\t" ) {
49
+ if (options.delimiter === "\t") {
37
50
  options.quote = null;
38
51
  }
39
52
  return options;
40
53
  }
41
54
 
42
55
  /**
43
- * @param {string} dir
44
- * @param {string} fileName
45
- * @param {boolean|undefined} [columns]
46
- * @param {string|undefined} [delimiter]
56
+ * @param {string} dir
57
+ * @param {string} fileName
58
+ * @param {boolean|import("csv-parse").ColumnOption[]|function (string[]):string[]} [columns]
59
+ * @param {string} [delimiter]
47
60
  */
48
- static parseFile( dir, fileName, columns = true, delimiter ) {
49
- const content = fs.readFileSync( dir + "/" + fileName );
61
+ static parseFile(dir, fileName, columns = true, delimiter) {
62
+ const content = fs.readFileSync(dir + "/" + fileName);
50
63
 
51
- const options = this.#getOptions( fileName, columns, delimiter );
52
- return parseSync( content, options );
64
+ const options = this.#getOptions(fileName, columns, delimiter);
65
+ return parseSync(content, options);
53
66
  }
54
67
 
55
68
  /**
56
- * @param {string} dir
57
- * @param {string} fileName
58
- * @param {boolean|undefined} columns
59
- * @param {string|undefined} delimiter
60
- * @param {*} callback
69
+ * @param {string} dir
70
+ * @param {string} fileName
71
+ * @param {boolean|import("csv-parse").ColumnOption[]} columns
72
+ * @param {string|undefined} delimiter
73
+ * @param {function (any):void} callback
61
74
  */
62
- static async parseStream( dir, fileName, columns = true, delimiter, callback ) {
63
- const options = this.#getOptions( fileName, columns, delimiter );
75
+ static async parseStream(
76
+ dir,
77
+ fileName,
78
+ columns = true,
79
+ delimiter,
80
+ callback
81
+ ) {
82
+ const options = this.#getOptions(fileName, columns, delimiter);
64
83
  const processFile = async () => {
84
+ /** @type {string[][]} */
65
85
  const records = [];
66
86
  const parser = fs
67
- .createReadStream( dir + "/" + fileName )
68
- .pipe( parse( options ) );
69
- parser.on( "readable", function () {
87
+ .createReadStream(dir + "/" + fileName)
88
+ .pipe(parse(options));
89
+ parser.on("readable", function () {
70
90
  let record;
71
- while ( ( record = parser.read() ) !== null ) {
72
- callback( record );
91
+ while ((record = parser.read()) !== null) {
92
+ callback(record);
73
93
  }
74
- } );
75
- await finished( parser );
94
+ });
95
+ await finished(parser);
76
96
  return records;
77
97
  };
78
98
  // Parse the CSV content
79
99
  await processFile();
80
-
81
100
  }
82
-
83
101
  }
84
102
 
85
- export { CSV };
103
+ export { CSV };
@@ -9,34 +9,40 @@ const MEDIA_TYPES = {
9
9
  XHTML: "application/xhtml+xml",
10
10
  };
11
11
 
12
+ /**
13
+ * @type {Object<string,string>}
14
+ */
12
15
  const EXT_MEDIA_MAP = {
13
- "jpeg": MEDIA_TYPES.JPG,
14
- "jpg": MEDIA_TYPES.JPG,
15
- "svg": MEDIA_TYPES.SVG,
16
+ jpeg: MEDIA_TYPES.JPG,
17
+ jpg: MEDIA_TYPES.JPG,
18
+ svg: MEDIA_TYPES.SVG,
16
19
  };
17
20
 
18
21
  class EBook {
19
-
20
22
  #outputDir;
21
23
  #filename;
22
24
  #pub_id;
23
25
  #title;
24
26
 
25
- constructor( outputDir, filename, pub_id, title ) {
26
-
27
+ /**
28
+ *
29
+ * @param {string} outputDir
30
+ * @param {string} filename
31
+ * @param {string} pub_id
32
+ * @param {string} title
33
+ */
34
+ constructor(outputDir, filename, pub_id, title) {
27
35
  this.#outputDir = outputDir;
28
36
  this.#filename = filename;
29
37
  this.#pub_id = pub_id;
30
38
  this.#title = title;
31
39
 
32
40
  // Initialize output directory.
33
- fs.rmSync( this.#outputDir, { force: true, recursive: true } );
34
- fs.mkdirSync( this.getContentDir(), { recursive: true } );
35
-
41
+ fs.rmSync(this.#outputDir, { force: true, recursive: true });
42
+ fs.mkdirSync(this.getContentDir(), { recursive: true });
36
43
  }
37
44
 
38
45
  async create() {
39
-
40
46
  const contentDir = this.getContentDir();
41
47
 
42
48
  this.#createContainerFile();
@@ -44,36 +50,35 @@ class EBook {
44
50
  this.#createPackageFile();
45
51
 
46
52
  // Copy assets
47
- // const cssDirTarget = contentDir + "/css";
48
- // fs.mkdirSync( cssDirTarget, { recursive: true } );
49
- fs.cpSync( Config.getPackageDir() + "/ebook", contentDir, { recursive: true } );
53
+ fs.cpSync(Config.getPackageDir() + "/ebook", contentDir, {
54
+ recursive: true,
55
+ });
50
56
 
51
57
  this.createZip();
52
-
53
58
  }
54
59
 
55
60
  #createContainerFile() {
56
-
57
61
  const metaDir = this.#getMetaDir();
58
62
 
59
- fs.mkdirSync( metaDir, { recursive: true } );
63
+ fs.mkdirSync(metaDir, { recursive: true });
60
64
 
61
- let xml = "<?xml version=\"1.0\"?>"
62
- + "<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">";
65
+ let xml =
66
+ '<?xml version="1.0"?>' +
67
+ '<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">';
63
68
 
64
- xml += "<rootfiles><rootfile full-path=\"epub/package.opf\" media-type=\"application/oebps-package+xml\" /></rootfiles>";
69
+ xml +=
70
+ '<rootfiles><rootfile full-path="epub/package.opf" media-type="application/oebps-package+xml" /></rootfiles>';
65
71
  xml += "</container>";
66
72
 
67
- fs.writeFileSync( metaDir + "/container.xml", xml );
68
-
73
+ fs.writeFileSync(metaDir + "/container.xml", xml);
69
74
  }
70
75
 
71
76
  #createPackageFile() {
72
-
73
77
  const dir = this.getContentDir();
74
78
 
75
- let xml = "<?xml version=\"1.0\"?>\n"
76
- + "<package version=\"3.0\" xml:lang=\"en\" xmlns=\"http://www.idpf.org/2007/opf\" unique-identifier=\"pub-id\">";
79
+ let xml =
80
+ '<?xml version="1.0"?>\n' +
81
+ '<package version="3.0" xml:lang="en" xmlns="http://www.idpf.org/2007/opf" unique-identifier="pub-id">';
77
82
 
78
83
  xml += this.#renderMetadata();
79
84
  xml += this.#renderManifest();
@@ -81,50 +86,67 @@ class EBook {
81
86
 
82
87
  xml += "</package>";
83
88
 
84
- fs.writeFileSync( dir + "/package.opf", xml );
85
-
89
+ fs.writeFileSync(dir + "/package.opf", xml);
86
90
  }
87
91
 
88
92
  async createPages() {
89
- throw new Error( "must be implemented by subclass" );
93
+ throw new Error("must be implemented by subclass");
90
94
  }
91
95
 
92
96
  createZip() {
93
97
  // Create zip.
94
98
  const filename = this.#outputDir + "/" + this.#filename + ".epub";
95
- const output = fs.createWriteStream( filename );
96
- const archive = archiver(
97
- "zip",
98
- {
99
- zlib: { level: 9 } // Sets the compression level.
100
- }
101
- );
99
+ const output = fs.createWriteStream(filename);
100
+ const archive = archiver("zip", {
101
+ zlib: { level: 9 }, // Sets the compression level.
102
+ });
102
103
 
103
- archive.pipe( output );
104
+ archive.pipe(output);
104
105
 
105
- archive.append( "application/epub+zip", { name: "mimetype", store: true } );
106
+ archive.append("application/epub+zip", {
107
+ name: "mimetype",
108
+ store: true,
109
+ });
106
110
 
107
- archive.directory( this.#getMetaDir(), "META-INF" );
108
- archive.directory( this.getContentDir(), "epub" );
111
+ archive.directory(this.#getMetaDir(), "META-INF");
112
+ archive.directory(this.getContentDir(), "epub");
109
113
 
110
114
  archive.finalize();
111
-
112
115
  }
113
116
 
114
117
  getContentDir() {
115
118
  return this.#outputDir + "/epub";
116
119
  }
117
120
 
118
- static getManifestEntry( id, href, mediaType = MEDIA_TYPES.XHTML ) {
119
- return "<item id=\"" + id + "\" href=\"" + href + "\" media-type=\"" + mediaType + "\" />";
121
+ /**
122
+ * @param {string} id
123
+ * @param {string} href
124
+ * @param {string} mediaType
125
+ */
126
+ static getManifestEntry(id, href, mediaType = MEDIA_TYPES.XHTML) {
127
+ return (
128
+ '<item id="' +
129
+ id +
130
+ '" href="' +
131
+ href +
132
+ '" media-type="' +
133
+ mediaType +
134
+ '" />'
135
+ );
120
136
  }
121
137
 
122
- static getMediaTypeForExt( ext ) {
123
- return EXT_MEDIA_MAP[ ext ];
138
+ /**
139
+ * @param {string} ext
140
+ */
141
+ static getMediaTypeForExt(ext) {
142
+ return EXT_MEDIA_MAP[ext];
124
143
  }
125
144
 
126
- static getSpineEntry( id ) {
127
- return "<itemref idref=\"" + id + "\"/>";
145
+ /**
146
+ * @param {string} id
147
+ */
148
+ static getSpineEntry(id) {
149
+ return '<itemref idref="' + id + '"/>';
128
150
  }
129
151
 
130
152
  #getMetaDir() {
@@ -133,38 +155,43 @@ class EBook {
133
155
 
134
156
  #renderManifest() {
135
157
  let xml = "<manifest>";
136
- xml += "<item id=\"c0\" href=\"css/main.css\" media-type=\"text/css\" />";
158
+ xml += '<item id="c0" href="css/main.css" media-type="text/css" />';
137
159
  xml += this.renderManifestEntries();
138
- xml += "<item id=\"toc\" href=\"toc.xhtml\" media-type=\"application/xhtml+xml\" properties=\"nav\" />";
160
+ xml +=
161
+ '<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml" properties="nav" />';
139
162
  return xml + "</manifest>";
140
163
  }
141
164
 
142
165
  renderManifestEntries() {
143
- throw new Error( "must be implemented by subclass" );
166
+ throw new Error("must be implemented by subclass");
144
167
  }
145
168
 
146
169
  #renderMetadata() {
147
- let xml = "<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\">";
148
- xml += XHTML.textElement( "dc:identifier", this.#pub_id, { id: "pub-id" } );
170
+ let xml = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">';
171
+ xml += XHTML.textElement("dc:identifier", this.#pub_id, {
172
+ id: "pub-id",
173
+ });
149
174
  xml += "<dc:language>en-US</dc:language>";
150
- xml += XHTML.textElement( "dc:title", this.#title );
175
+ xml += XHTML.textElement("dc:title", this.#title);
151
176
  const d = new Date();
152
- d.setUTCMilliseconds( 0 );
153
- xml += "<meta property=\"dcterms:modified\">" + d.toISOString().replace( ".000", "" ) + "</meta>";
177
+ d.setUTCMilliseconds(0);
178
+ xml +=
179
+ '<meta property="dcterms:modified">' +
180
+ d.toISOString().replace(".000", "") +
181
+ "</meta>";
154
182
  return xml + "</metadata>";
155
183
  }
156
184
 
157
185
  #renderSpine() {
158
186
  let xml = "<spine>";
159
- xml += "<itemref idref=\"toc\"/>";
187
+ xml += '<itemref idref="toc"/>';
160
188
  xml += this.renderSpineElements();
161
189
  return xml + "</spine>";
162
190
  }
163
191
 
164
192
  renderSpineElements() {
165
- throw new Error( "must be implemented by subclass" );
193
+ throw new Error("must be implemented by subclass");
166
194
  }
167
-
168
195
  }
169
196
 
170
- export { EBook };
197
+ export { EBook };
@@ -1,22 +1,27 @@
1
1
  import * as fs from "node:fs";
2
2
 
3
3
  class EBookPage {
4
-
5
4
  #fileName;
6
5
  #title;
7
6
  #rootPrefix;
8
7
 
9
- constructor( fileName, title, rootPrefix = "" ) {
8
+ /**
9
+ *
10
+ * @param {string} fileName
11
+ * @param {string} title
12
+ * @param {string} rootPrefix
13
+ */
14
+ constructor(fileName, title, rootPrefix = "") {
10
15
  this.#fileName = fileName;
11
16
  this.#title = title;
12
17
  this.#rootPrefix = rootPrefix;
13
18
  }
14
19
 
15
20
  create() {
16
- let html = this.#renderPageStart( this.#title );
21
+ let html = this.#renderPageStart(this.#title);
17
22
  html += this.renderPageBody();
18
23
  html += this.#renderPageEnd();
19
- fs.writeFileSync( this.#fileName, html );
24
+ fs.writeFileSync(this.#fileName, html);
20
25
  }
21
26
 
22
27
  getTitle() {
@@ -28,22 +33,28 @@ class EBookPage {
28
33
  }
29
34
 
30
35
  renderPageBody() {
31
- throw new Error( "must be implemented by subclass" );
36
+ throw new Error("must be implemented by subclass");
32
37
  }
33
38
 
34
39
  #renderPageEnd() {
35
40
  return "</body></html>";
36
41
  }
37
42
 
38
- #renderPageStart( title ) {
39
- let html = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
40
- html += "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
43
+ /**
44
+ * @param {string} title
45
+ */
46
+ #renderPageStart(title) {
47
+ let html = '<?xml version="1.0" encoding="utf-8"?>\n';
48
+ html +=
49
+ '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">';
41
50
  html += "<head><title>" + title + "</title>";
42
- html += "<link href=\"" + this.#rootPrefix + "css/main.css\" rel=\"stylesheet\" />";
51
+ html +=
52
+ '<link href="' +
53
+ this.#rootPrefix +
54
+ 'css/main.css" rel="stylesheet" />';
43
55
  html += "</head>" + this.#renderBodyStart();
44
56
  return html;
45
57
  }
46
-
47
58
  }
48
59
 
49
- export { EBookPage };
60
+ export { EBookPage };