@ca-plant-list/ca-plant-list 0.4.26 → 0.4.28

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 (58) hide show
  1. package/data/exceptions.json +0 -27
  2. package/data/genera.json +52 -28
  3. package/data/glossary/anther.md +3 -0
  4. package/data/glossary/filament.md +3 -0
  5. package/data/glossary/stamen.md +3 -0
  6. package/data/illustrations/inkscape/stamen.svg +132 -0
  7. package/data/inatobsphotos.csv +0 -5
  8. package/data/inattaxonphotos.csv +69 -64
  9. package/data/synonyms.csv +155 -164
  10. package/data/taxa.csv +38 -38
  11. package/data/text/Dichelostemma-congestum.md +1 -0
  12. package/data/text/Dipterostemon-capitatus-subsp-capitatus.md +1 -0
  13. package/generators/eleventy/layouts/h1.njk +6 -0
  14. package/generators/eleventy/layouts/html.njk +55 -0
  15. package/lib/basepagerenderer.js +35 -15
  16. package/lib/config.js +5 -3
  17. package/lib/ebook/ebookpage.js +1 -4
  18. package/lib/ebook/ebooksitegenerator.js +4 -12
  19. package/lib/ebook/glossarypages.js +1 -3
  20. package/lib/ebook/plantbook.js +1 -1
  21. package/lib/files.js +1 -0
  22. package/lib/htmltaxon.js +8 -19
  23. package/lib/index.d.ts +20 -8
  24. package/lib/index.js +4 -2
  25. package/lib/jekyll.js +40 -59
  26. package/lib/markdown.js +3 -5
  27. package/lib/sitegenerator.js +68 -12
  28. package/lib/taxonomy/taxon.js +5 -1
  29. package/lib/tools/cch2.js +37 -18
  30. package/lib/tools/jepsonfamilies.js +1 -1
  31. package/lib/types.js +4 -0
  32. package/lib/utils/eleventyGenerator.js +82 -0
  33. package/lib/utils/htmlFragments.js +19 -0
  34. package/lib/web/glossarypages.js +6 -10
  35. package/lib/web/pageFamily.js +14 -14
  36. package/lib/web/pageGeneric.js +78 -0
  37. package/lib/web/{pagetaxon.js → pageTaxon.js} +4 -4
  38. package/lib/web/pageTaxonList.js +53 -0
  39. package/lib/{pagerenderer.js → web/renderAllPages.js} +38 -80
  40. package/package.json +12 -10
  41. package/scripts/build-site.js +20 -52
  42. package/scripts/cpl-photos.js +61 -24
  43. package/scripts/cpl-tools.js +4 -1
  44. package/static/assets/js/nameSearchData.js +2 -0
  45. package/{jekyll → static}/assets/js/name_search.js +12 -14
  46. package/static/name_search.html +15 -0
  47. package/jekyll/_includes/glossary.html +0 -0
  48. package/jekyll/name_search.html +0 -17
  49. package/lib/genericpage.js +0 -88
  50. /package/{jekyll → generators}/_includes/analytics.html +0 -0
  51. /package/{jekyll → generators}/_includes/menu_extra.html +0 -0
  52. /package/{jekyll → generators/jekyll}/_config.yml +0 -0
  53. /package/{jekyll → generators/jekyll}/_layouts/default.html +0 -0
  54. /package/{jekyll → generators/jekyll}/_layouts/html.html +0 -0
  55. /package/{jekyll → static}/assets/css/main.css +0 -0
  56. /package/{jekyll → static}/assets/js/ui.js +0 -0
  57. /package/{jekyll → static}/assets/js/utils.js +0 -0
  58. /package/{jekyll → static}/index.md +0 -0
package/lib/index.d.ts CHANGED
@@ -15,6 +15,10 @@ type RefSourceCode =
15
15
  | "jepson"
16
16
  | "rpi";
17
17
 
18
+ type SiteGeneratorOptions = {
19
+ passThroughPatterns?: string[];
20
+ };
21
+
18
22
  type TaxaColDef<T> = {
19
23
  title: string;
20
24
  class?: string;
@@ -53,8 +57,13 @@ export type TaxonOverrides = {
53
57
  // Classes
54
58
 
55
59
  export class BasePageRenderer {
56
- static renderBasePages<T extends Taxon>(
60
+ static newSiteGenerator(
61
+ config: Config,
57
62
  outputDir: string,
63
+ options?: SiteGeneratorOptions,
64
+ ): SiteGenerator;
65
+ static renderBasePages<T extends Taxon>(
66
+ siteGenerator: SiteGenerator,
58
67
  taxa: Taxa<T>,
59
68
  familyCols?: TaxaColDef<T>[],
60
69
  ): void;
@@ -150,6 +159,10 @@ export class HTML {
150
159
  ): string;
151
160
  }
152
161
 
162
+ export class HTMLFragments {
163
+ static getMarkdownSection(filePath: string): string;
164
+ }
165
+
153
166
  export class HTMLTaxon {
154
167
  static addLink(
155
168
  links: string[],
@@ -174,13 +187,6 @@ export class HTMLTaxon {
174
187
  header: string,
175
188
  className?: string,
176
189
  ): string;
177
- static getMarkdownSection(filePath: string): string;
178
- }
179
-
180
- export class Jekyll {
181
- static hasInclude(baseDir: string, path: string): boolean;
182
- static include(fileName: string): string;
183
- static writeInclude(baseDir: string, path: string, data: string): void;
184
190
  }
185
191
 
186
192
  export class Photo {
@@ -196,6 +202,12 @@ export class Program {
196
202
  static getProgram(): Command;
197
203
  }
198
204
 
205
+ export class SiteGenerator {
206
+ generate(outputDir: string): Promise<void>;
207
+ getBaseDir(): string;
208
+ getFrontMatter(atts: Record<string, string | undefined>): string;
209
+ }
210
+
199
211
  export class Taxa<T> {
200
212
  constructor(
201
213
  inclusionList: Record<string, TaxonOverrides> | true,
package/lib/index.js CHANGED
@@ -7,10 +7,11 @@ import { ExternalSites } from "./externalsites.js";
7
7
  import { Families } from "./taxonomy/families.js";
8
8
  import { Files } from "./files.js";
9
9
  import { HTML } from "./html.js";
10
+ import { HTMLFragments } from "./utils/htmlFragments.js";
10
11
  import { HTMLTaxon } from "./htmltaxon.js";
11
- import { Jekyll } from "./jekyll.js";
12
12
  import { PlantBook } from "./ebook/plantbook.js";
13
13
  import { Program } from "./program.js";
14
+ import { SiteGenerator } from "./sitegenerator.js";
14
15
  import { Taxa } from "./taxonomy/taxa.js";
15
16
  import { Taxon } from "./taxonomy/taxon.js";
16
17
 
@@ -24,10 +25,11 @@ export {
24
25
  Families,
25
26
  Files,
26
27
  HTML,
28
+ HTMLFragments,
27
29
  HTMLTaxon,
28
- Jekyll,
29
30
  PlantBook,
30
31
  Program,
32
+ SiteGenerator,
31
33
  Taxa,
32
34
  Taxon,
33
35
  };
package/lib/jekyll.js CHANGED
@@ -1,71 +1,52 @@
1
+ import * as child_process from "node:child_process";
2
+ import * as path from "node:path";
1
3
  import { Files } from "./files.js";
2
4
  import { SiteGenerator } from "./sitegenerator.js";
3
-
4
- const FRONT_DELIM = "---";
5
-
6
- class Jekyll extends SiteGenerator {
7
- /**
8
- * @param {string} baseDir
9
- */
10
- constructor(baseDir) {
11
- super(baseDir);
5
+ import { Config } from "./config.js";
6
+
7
+ export class Jekyll extends SiteGenerator {
8
+ copyGeneratorFiles() {
9
+ // First copy default files from package.
10
+ Files.copyDir(
11
+ path.join(Config.getPackageDir(), "./generators/jekyll"),
12
+ this.getBaseDir(),
13
+ );
14
+ // Then copy files from current dir (which may override default files).
15
+ Files.copyDir("./generators/jekyll", this.getBaseDir());
12
16
  }
13
17
 
14
- /**
15
- * @param {Object<string,string>} atts
16
- */
17
- static getFrontMatter(atts) {
18
- const lines = [FRONT_DELIM];
19
- for (const [k, v] of Object.entries(atts)) {
20
- lines.push(k + ': "' + v + '"');
18
+ async generate() {
19
+ /**
20
+ * @param {string[]} configFiles
21
+ * @param {string} dir
22
+ * @param {string} name
23
+ */
24
+ function addConfigFile(configFiles, dir, name) {
25
+ const fullPath = path.join(dir, name);
26
+ if (Files.exists(fullPath)) {
27
+ configFiles.push(fullPath);
28
+ }
21
29
  }
22
- if (!atts.layout) {
23
- lines.push("layout: default");
24
- }
25
- lines.push(FRONT_DELIM);
26
- return lines.join("\n") + "\n";
27
- }
28
30
 
29
- /**
30
- * @param {string} baseDir
31
- * @param {string} path
32
- */
33
- static hasInclude(baseDir, path) {
34
- return Files.exists(baseDir + "/_includes/" + path);
35
- }
31
+ const srcDir = "./output";
32
+ const destDir = "./public";
36
33
 
37
- /**
38
- * @param {string} path
39
- */
40
- static include(path) {
41
- // This works for .md includes; should have conditional logic to detect other types.
42
- return (
43
- "{% capture my_include %}{% include " +
44
- path +
45
- " %}{% endcapture %}{{ my_include | markdownify }}"
46
- );
47
- }
34
+ // Remove existing files.
35
+ Files.rmDir(destDir);
48
36
 
49
- /**
50
- * @param {string} baseDir
51
- * @param {string} path
52
- * @param {string} data
53
- */
54
- static writeInclude(baseDir, path, data) {
55
- Files.write(baseDir + "/_includes/" + path, data);
56
- }
37
+ const options = ["--source", srcDir, "--destination", destDir];
57
38
 
58
- /**
59
- * @param {string} content
60
- * @param {Object<string,string>} attributes
61
- * @param {string} filename
62
- */
63
- writeTemplate(content, attributes, filename) {
64
- Files.write(
65
- Files.join(this.getBaseDir(), filename),
66
- Jekyll.getFrontMatter(attributes) + content
39
+ // Find out what config files are available.
40
+ /** @type {string[]} */
41
+ const configFiles = [];
42
+ addConfigFile(configFiles, srcDir, "_config.yml");
43
+ addConfigFile(configFiles, srcDir, "_config-local.yml");
44
+ addConfigFile(configFiles, ".", "_config-dev.yml");
45
+ options.push("--config", `"${configFiles.join()}"`);
46
+
47
+ const result = child_process.execSync(
48
+ "bundle exec jekyll build " + options.join(" "),
67
49
  );
50
+ console.log(result.toString());
68
51
  }
69
52
  }
70
-
71
- export { Jekyll };
package/lib/markdown.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import markdownIt from "markdown-it";
2
2
  import { Files } from "./files.js";
3
3
 
4
- class Markdown {
4
+ export class Markdown {
5
5
  static #md = new markdownIt({ xhtmlOut: true });
6
6
 
7
7
  /**
8
8
  * @param {string} filePath
9
- * @returns {string|undefined}
9
+ * @returns {string}
10
10
  */
11
11
  static fileToHTML(filePath) {
12
12
  if (!Files.exists(filePath)) {
13
- return;
13
+ return "";
14
14
  }
15
15
  return this.strToHTML(Files.read(filePath));
16
16
  }
@@ -22,5 +22,3 @@ class Markdown {
22
22
  return this.#md.render(str);
23
23
  }
24
24
  }
25
-
26
- export { Markdown };
@@ -1,16 +1,29 @@
1
+ import path from "node:path";
1
2
  import { optimize } from "svgo-ll";
2
3
 
3
4
  import { Config } from "./config.js";
4
5
  import { Files } from "./files.js";
5
6
 
6
- class SiteGenerator {
7
+ const FRONT_DELIM = "---";
8
+
9
+ export class SiteGenerator {
10
+ #config;
7
11
  #baseDir;
12
+ #options;
8
13
 
9
14
  /**
15
+ * @param {Config} config
10
16
  * @param {string} baseDir
17
+ * @param {import("./index.js").SiteGeneratorOptions} [options]
11
18
  */
12
- constructor(baseDir) {
19
+ constructor(config, baseDir, options = {}) {
20
+ this.#config = config;
13
21
  this.#baseDir = baseDir;
22
+ this.#options = options;
23
+ }
24
+
25
+ copyGeneratorFiles() {
26
+ throw new Error("must be implemented by subclass");
14
27
  }
15
28
 
16
29
  /**
@@ -23,11 +36,11 @@ class SiteGenerator {
23
36
  */
24
37
  function createFlowerColorIcons(outputDir, flowerColors) {
25
38
  // Read generic input.
26
- const inputFileName = Files.join(outputDir, "flower.svg");
39
+ const inputFileName = path.join(outputDir, "flower.svg");
27
40
  const srcSVG = Files.read(inputFileName);
28
41
  for (const color of flowerColors) {
29
42
  Files.write(
30
- Files.join(outputDir, "f-" + color.getColorName() + ".svg"),
43
+ path.join(outputDir, "f-" + color.getColorName() + ".svg"),
31
44
  srcSVG.replace("#ff0", color.getColorCode()),
32
45
  );
33
46
  }
@@ -59,26 +72,69 @@ class SiteGenerator {
59
72
  createFlowerColorIcons(outputDir, flowerColors);
60
73
  }
61
74
 
75
+ copyStaticFiles() {
76
+ // First copy default files from ca-plant-list.
77
+ Files.copyDir(
78
+ path.join(Config.getPackageDir(), "static"),
79
+ this.#baseDir,
80
+ );
81
+ // Then copy files from current directory (which may override default files).
82
+ Files.copyDir("./static", this.#baseDir);
83
+ }
84
+
85
+ /**
86
+ * @param {string} webDir
87
+ */
88
+ // eslint-disable-next-line no-unused-vars
89
+ async generate(webDir) {
90
+ throw new Error("must be implemented by subclass");
91
+ }
92
+
62
93
  getBaseDir() {
63
94
  return this.#baseDir;
64
95
  }
65
96
 
97
+ getConfig() {
98
+ return this.#config;
99
+ }
100
+
66
101
  /**
67
- * @param {string} path
102
+ * @param {Object<string,string|undefined>} atts
68
103
  */
69
- mkdir(path) {
70
- Files.mkdir(Files.join(this.#baseDir, path));
104
+ getFrontMatter(atts) {
105
+ const lines = [FRONT_DELIM];
106
+ for (const [k, v] of Object.entries(atts)) {
107
+ if (v) {
108
+ lines.push(k + ': "' + v + '"');
109
+ }
110
+ }
111
+ lines.push(FRONT_DELIM);
112
+ return lines.join("\n") + "\n\n";
113
+ }
114
+
115
+ /**
116
+ * @returns {string[]}
117
+ */
118
+ getPassThroughPatterns() {
119
+ return this.#options.passThroughPatterns ?? [];
120
+ }
121
+
122
+ /**
123
+ * @param {string} outputSubdir
124
+ */
125
+ mkdir(outputSubdir) {
126
+ Files.mkdir(path.join(this.#baseDir, outputSubdir));
71
127
  }
72
128
 
73
129
  /**
74
130
  * @param {string} content
75
- * @param {{title:string}} attributes
131
+ * @param {Object<string,string>} attributes
76
132
  * @param {string} filename
77
133
  */
78
- // eslint-disable-next-line no-unused-vars
79
134
  writeTemplate(content, attributes, filename) {
80
- throw new Error("must be implemented by subclass");
135
+ Files.write(
136
+ path.join(this.getBaseDir(), filename),
137
+ this.getFrontMatter(attributes) + content,
138
+ );
81
139
  }
82
140
  }
83
-
84
- export { SiteGenerator };
@@ -282,7 +282,11 @@ class Taxon extends Taxonomy {
282
282
  }
283
283
 
284
284
  isCANative() {
285
- return this.#status === "N" || this.#status === "NC";
285
+ return (
286
+ this.#status === "N" ||
287
+ this.#status === "NC" ||
288
+ this.#status === "U"
289
+ );
286
290
  }
287
291
 
288
292
  /**
package/lib/tools/cch2.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import path from "node:path";
2
+ import { renameSync } from "node:fs";
3
+ import puppeteer from "puppeteer";
2
4
  import { CSV } from "../csv.js";
3
5
  import { TaxaCSV } from "./taxacsv.js";
4
6
  import { Files } from "../files.js";
5
- import puppeteer from "puppeteer";
6
- import { renameSync } from "node:fs";
7
7
 
8
8
  /**
9
9
  * @typedef {{id:string}} CCHTaxon
@@ -29,12 +29,12 @@ export class CCH2 {
29
29
  ) {
30
30
  const toolsDataPath = path.join(toolsDataDir, "cch2");
31
31
 
32
- const cchTaxa = await getCCHTaxa(toolsDataPath, taxa);
32
+ const cchTaxa = await getCCHTaxa(toolsDataPath);
33
33
 
34
34
  const idsToUpdate = new Map();
35
35
  for (const taxon of taxa.getTaxonList()) {
36
36
  const name = taxon.getName();
37
- const cchTaxon = cchTaxa.get(name);
37
+ const cchTaxon = getCCHTaxon(taxon, cchTaxa);
38
38
  if (!cchTaxon) {
39
39
  if (!exceptions.hasException(name, "cch", "notincch")) {
40
40
  errorLog.log(name, "not found in CCH data");
@@ -106,27 +106,18 @@ export class CCH2 {
106
106
 
107
107
  /**
108
108
  * @param {string} toolsDataPath
109
- * @param {import("../types.js").Taxa} taxa
110
109
  * @returns {Promise<CCHTaxa>}
111
110
  */
112
- async function getCCHTaxa(toolsDataPath, taxa) {
111
+ async function getCCHTaxa(toolsDataPath) {
113
112
  /**
114
- * @param {{taxonID:string,scientificName:string,rankID:string,acceptance:"0"|"1",acceptedTaxonID:string}} record
113
+ * @param {{taxonID:string,scientificName:string,rankID:string,acceptance:"0"|"1",acceptedTaxonID:string,acceptedScientificName:string}} record
115
114
  */
116
115
  function callback(record) {
117
116
  if (parseInt(record.rankID) < 220) {
118
117
  // Ignore ranks above species.
119
118
  return;
120
119
  }
121
- if (!taxa.getTaxon(record.scientificName)) {
122
- // If we're not tracking the taxon, ignore it.
123
- return;
124
- }
125
- if (record.acceptance !== "1" && data.has(record.scientificName)) {
126
- // Only add the synonym if there is no main entry.
127
- return;
128
- }
129
- data.set(record.scientificName, { id: record.acceptedTaxonID });
120
+ cchTaxa.set(record.scientificName, { id: record.taxonID });
130
121
  }
131
122
 
132
123
  const fileName = path.join(toolsDataPath, "taxa.csv");
@@ -134,11 +125,39 @@ async function getCCHTaxa(toolsDataPath, taxa) {
134
125
  await retrieveDataFile(toolsDataPath);
135
126
  }
136
127
 
137
- const data = new Map();
128
+ const cchTaxa = new Map();
138
129
 
139
130
  await CSV.parseFileStream(fileName, callback);
140
131
 
141
- return data;
132
+ return cchTaxa;
133
+ }
134
+
135
+ /**
136
+ * @param {import("../types.js").Taxon} taxon
137
+ * @param {CCHTaxa} cchTaxa
138
+ * @returns {CCHTaxon|undefined}
139
+ */
140
+ function getCCHTaxon(taxon, cchTaxa) {
141
+ /**
142
+ * @param {string} name
143
+ */
144
+ function getTaxon(name) {
145
+ return cchTaxa.get(name);
146
+ }
147
+
148
+ const name = taxon.getName();
149
+ const cchTaxon = getTaxon(name);
150
+ if (cchTaxon) {
151
+ return cchTaxon;
152
+ }
153
+
154
+ // No reference for the current name; try synonyms and see if any of them work.
155
+ for (const synonym of taxon.getSynonyms()) {
156
+ const cchTaxon = getTaxon(synonym);
157
+ if (cchTaxon) {
158
+ return cchTaxon;
159
+ }
160
+ }
142
161
  }
143
162
 
144
163
  /**
@@ -8,7 +8,7 @@ export class JepsonFamilies {
8
8
  * @param {string} outputdir
9
9
  */
10
10
  static async build(toolsDataDir, outputdir) {
11
- const url = "https://ucjeps.berkeley.edu/eflora/toc.html";
11
+ const url = "https://ucjeps.berkeley.edu/IJM_keys/toc.html";
12
12
  const indexFileName = path.basename(url);
13
13
  const toolsDataPath = toolsDataDir + "/jepsonfam";
14
14
  const indexFilePath = toolsDataPath + "/" + indexFileName;
package/lib/types.js CHANGED
@@ -1,8 +1,12 @@
1
1
  /**
2
+ * Types
3
+ * @typedef {{t:string,c?:string,s?:string[]}} NameSearchData
4
+ *
2
5
  * Classes
3
6
  * @typedef {import("./config.js").Config} Config
4
7
  * @typedef {import("./taxonomy/families.js").Families} Families
5
8
  * @typedef {import("./taxonomy/families.js").Family} Family
9
+ * @typedef {import("./sitegenerator.js").SiteGenerator} SiteGenerator
6
10
  * @typedef {import("./taxonomy/taxa.js").Taxa} Taxa
7
11
  * @typedef {import("./index.js").TaxaColDef<import("./types.js").Taxon>} TaxaColDef
8
12
  * @typedef {import("./taxonomy/taxon.js").Taxon} Taxon
@@ -0,0 +1,82 @@
1
+ // @ts-ignore
2
+ import { Eleventy } from "@11ty/eleventy";
3
+ import { SiteGenerator } from "../sitegenerator.js";
4
+ import path from "node:path";
5
+ import { Files } from "../files.js";
6
+ import { Config } from "../config.js";
7
+
8
+ export class EleventyGenerator extends SiteGenerator {
9
+ copyGeneratorFiles() {
10
+ // First copy default files from package.
11
+ const layoutSrc = "./generators/eleventy/layouts";
12
+ const commonSrc = "./generators/_includes";
13
+ const dest = path.join(this.getBaseDir(), "_includes");
14
+
15
+ Files.copyDir(path.join(Config.getPackageDir(), commonSrc), dest);
16
+ Files.copyDir(path.join(Config.getPackageDir(), layoutSrc), dest);
17
+
18
+ // Then copy files from current dir (which may override default files).
19
+ if (Files.isDir(commonSrc)) {
20
+ Files.copyDir(commonSrc, dest);
21
+ }
22
+ if (Files.isDir(layoutSrc)) {
23
+ Files.copyDir(layoutSrc, dest);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * @param {string} webDir
29
+ */
30
+ async generate(webDir) {
31
+ const srcDir = this.getBaseDir();
32
+ const config = this.getConfig();
33
+ const generator = this;
34
+ let elev = new Eleventy(srcDir, webDir, {
35
+ quietMode: true,
36
+ config:
37
+ // @ts-ignore
38
+ function (eleventyConfig) {
39
+ // Not running in project root, so using .gitignore will break things.
40
+ eleventyConfig.setUseGitIgnore(false);
41
+
42
+ // Don't change file system structure when writing output files.
43
+ eleventyConfig.addGlobalData("permalink", () => {
44
+ // @ts-ignore
45
+ return (data) => {
46
+ // Include directories in the generated content.
47
+ const inputPath = path.relative(
48
+ srcDir,
49
+ data.page.inputPath,
50
+ );
51
+ // Remove the file extension.
52
+ const parsed = path.parse(inputPath);
53
+ return path.join(parsed.dir, `${parsed.name}.html`);
54
+ };
55
+ });
56
+
57
+ // Use layout with <h1> by default.
58
+ eleventyConfig.addGlobalData("layout", "h1.njk");
59
+
60
+ // Set site name for use in nav bar.
61
+ eleventyConfig.addGlobalData(
62
+ "siteName",
63
+ config.getSiteName(),
64
+ );
65
+
66
+ const passThroughPatterns = [
67
+ "assets",
68
+ "i",
69
+ "errors.tsv",
70
+ ...generator.getPassThroughPatterns(),
71
+ ];
72
+ for (const pattern of passThroughPatterns) {
73
+ eleventyConfig.addPassthroughCopy(
74
+ // Eleventy apparently can't handle windows paths in globs, so change it.
75
+ path.join(srcDir, pattern).replaceAll("\\", "/"),
76
+ );
77
+ }
78
+ },
79
+ });
80
+ await elev.write();
81
+ }
82
+ }
@@ -0,0 +1,19 @@
1
+ import { HTML } from "../html.js";
2
+ import { Markdown } from "../markdown.js";
3
+
4
+ /**
5
+ * Utilities to create HTML fragments specific to ca-plant-list.
6
+ */
7
+ export class HTMLFragments {
8
+ /**
9
+ * @param {string} filePath
10
+ * @returns {string}
11
+ */
12
+ static getMarkdownSection(filePath) {
13
+ const footerMarkdown = Markdown.fileToHTML(filePath);
14
+ if (footerMarkdown) {
15
+ return HTML.wrap("div", footerMarkdown, "section");
16
+ }
17
+ return "";
18
+ }
19
+ }
@@ -1,11 +1,11 @@
1
1
  import { Glossary } from "../plants/glossary.js";
2
2
  import { Markdown } from "../markdown.js";
3
3
  import { HTML } from "../html.js";
4
- import { Files } from "../files.js";
4
+ import path from "node:path";
5
5
 
6
6
  const ENTRY_DIR = "g";
7
7
 
8
- class GlossaryPages {
8
+ export class GlossaryPages {
9
9
  #siteGenerator;
10
10
  #glossary;
11
11
 
@@ -22,14 +22,13 @@ class GlossaryPages {
22
22
  */
23
23
  #generateEntryPage(entry) {
24
24
  const title = entry.getTermName();
25
- let html = HTML.textElement("h1", title);
26
- html += HTML.wrap("div", Markdown.strToHTML(entry.getMarkdown()), {
25
+ let html = HTML.wrap("div", Markdown.strToHTML(entry.getMarkdown()), {
27
26
  class: "glossary",
28
27
  });
29
28
  this.#siteGenerator.writeTemplate(
30
29
  html,
31
30
  { title: title },
32
- Files.join(ENTRY_DIR, title + ".html"),
31
+ path.posix.join(ENTRY_DIR, title + ".html"),
33
32
  );
34
33
  }
35
34
 
@@ -49,13 +48,12 @@ class GlossaryPages {
49
48
  for (const entry of entries) {
50
49
  links.push(
51
50
  HTML.getLink(
52
- Files.join(ENTRY_DIR, entry.getHTMLFileName()),
51
+ path.posix.join(ENTRY_DIR, entry.getHTMLFileName()),
53
52
  entry.getTermName(),
54
53
  ),
55
54
  );
56
55
  }
57
- let html = HTML.wrap("h1", "Glossary");
58
- html += HTML.wrap("ol", HTML.arrayToLI(links));
56
+ const html = HTML.wrap("ol", HTML.arrayToLI(links));
59
57
  this.#siteGenerator.writeTemplate(
60
58
  html,
61
59
  { title: "Glossary" },
@@ -72,5 +70,3 @@ class GlossaryPages {
72
70
  this.#generateEntryPages();
73
71
  }
74
72
  }
75
-
76
- export { GlossaryPages };