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

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 (42) hide show
  1. package/data/taxa.csv +1 -0
  2. package/generators/eleventy/layouts/h1.njk +6 -0
  3. package/generators/eleventy/layouts/html.njk +55 -0
  4. package/lib/basepagerenderer.js +35 -15
  5. package/lib/config.js +5 -3
  6. package/lib/ebook/ebookpage.js +1 -4
  7. package/lib/ebook/ebooksitegenerator.js +4 -12
  8. package/lib/ebook/glossarypages.js +1 -3
  9. package/lib/ebook/plantbook.js +1 -1
  10. package/lib/files.js +1 -0
  11. package/lib/htmltaxon.js +8 -19
  12. package/lib/index.d.ts +20 -8
  13. package/lib/index.js +4 -2
  14. package/lib/jekyll.js +40 -59
  15. package/lib/markdown.js +3 -5
  16. package/lib/sitegenerator.js +68 -12
  17. package/lib/types.js +4 -0
  18. package/lib/utils/eleventyGenerator.js +82 -0
  19. package/lib/utils/htmlFragments.js +19 -0
  20. package/lib/web/glossarypages.js +6 -10
  21. package/lib/web/pageFamily.js +14 -14
  22. package/lib/web/pageGeneric.js +78 -0
  23. package/lib/web/{pagetaxon.js → pageTaxon.js} +4 -4
  24. package/lib/web/pageTaxonList.js +53 -0
  25. package/lib/{pagerenderer.js → web/renderAllPages.js} +38 -80
  26. package/package.json +12 -10
  27. package/scripts/build-site.js +20 -52
  28. package/static/assets/js/nameSearchData.js +2 -0
  29. package/{jekyll → static}/assets/js/name_search.js +12 -14
  30. package/static/name_search.html +15 -0
  31. package/jekyll/_includes/glossary.html +0 -0
  32. package/jekyll/name_search.html +0 -17
  33. package/lib/genericpage.js +0 -88
  34. /package/{jekyll → generators}/_includes/analytics.html +0 -0
  35. /package/{jekyll → generators}/_includes/menu_extra.html +0 -0
  36. /package/{jekyll → generators/jekyll}/_config.yml +0 -0
  37. /package/{jekyll → generators/jekyll}/_layouts/default.html +0 -0
  38. /package/{jekyll → generators/jekyll}/_layouts/html.html +0 -0
  39. /package/{jekyll → static}/assets/css/main.css +0 -0
  40. /package/{jekyll → static}/assets/js/ui.js +0 -0
  41. /package/{jekyll → static}/assets/js/utils.js +0 -0
  42. /package/{jekyll → static}/index.md +0 -0
package/data/taxa.csv CHANGED
@@ -503,6 +503,7 @@ Cryptantha clevelandii var. florosa,,N,57120,11565,1045003,203153,,Coastal Crypt
503
503
  Cryptantha corollata,,N,21213,2452,58853,203157,,Coast Range Cryptantha
504
504
  Cryptantha flaccida,,N,21228,2460,56989,203167,,Flaccid Cryptantha
505
505
  Cryptantha hooveri,,N,21240,2468,57701,245944,,Hoover's Cryptantha,,,,,525,1A,,,SH,GH
506
+ Cryptantha hispidula,Napa cryptantha,N,21237,2465,57703,203175,,Napa Cryptantha,annual,white,3,5
506
507
  Cryptantha intermedia var. intermedia,,N,71933,11568,80627,203183
507
508
  Cryptantha juniperensis,rigid cryptantha,N,103476,14557,769572,203205
508
509
  Cryptantha micromeres,,N,21263,2476,770232,203194,,Minute-flowered Cryptantha
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: html.njk
3
+ ---
4
+
5
+ <h1>{{title}}</h1>
6
+ {{content|safe}}
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
8
+ <title>{{title}}</title>
9
+ <link href="/assets/css/main.css" rel="stylesheet">
10
+ </head>
11
+
12
+ <body>
13
+
14
+ <nav class="navbar navbar-expand-md">
15
+ <div class="container-xxl px-5">
16
+ <button id="hamburger" class="navbar-toggler" type="button" data-bs-toggle="collapse"
17
+ data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
18
+ aria-label="Toggle navigation">
19
+ <span class="navbar-toggler-icon"></span>
20
+ </button>
21
+ <a class="navbar-brand" href="/index.html">{{siteName}}</a>
22
+ <div class="collapse navbar-collapse" id="navbarSupportedContent">
23
+ <ul class="navbar-nav me-auto mb-lg-0">
24
+ <li class="nav-item">
25
+ <a class="nav-link" href="/index_lists.html">Plant Lists</a>
26
+ </li>
27
+ <li class="nav-item">
28
+ <a class="nav-link" href="/rare_plants.html">Rare Plants</a>
29
+ </li>
30
+ <li class="nav-item">
31
+ <a class="nav-link" href="/name_search.html">Name Search</a>
32
+ </li>
33
+ <li class="nav-item">
34
+ <a class="nav-link" href="/glossary.html">Glossary</a>
35
+ </li>
36
+ {%include "menu_extra.html" %}
37
+ </ul>
38
+ </div>
39
+ </div>
40
+ </nav>
41
+
42
+ <div class="container-xxl px-5">
43
+ {{content|safe}}
44
+ </div>
45
+
46
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
47
+
48
+ {%if js%}<script src="/assets/js/{{js}}" type="module"></script>{%endif%}
49
+
50
+ <script src="/assets/js/ui.js" type="module"></script>
51
+
52
+ {%include "analytics.html"%}
53
+ </body>
54
+
55
+ </html>
@@ -1,33 +1,49 @@
1
- import { Config } from "./config.js";
1
+ import path from "node:path";
2
2
  import { Files } from "./files.js";
3
- import { Jekyll } from "./jekyll.js";
4
3
  import { GlossaryPages } from "./web/glossarypages.js";
5
4
  import { PageFamilyList } from "./web/pageFamily.js";
5
+ import { Jekyll } from "./jekyll.js";
6
+ import { EleventyGenerator } from "./utils/eleventyGenerator.js";
6
7
 
7
8
  export class BasePageRenderer {
8
9
  /**
10
+ * @param {import("./types.js").Config} config
9
11
  * @param {string} outputDir
12
+ * @param {import("./index.js").SiteGeneratorOptions} options
13
+ * @param {"11ty"|"jekyll"} [type="11ty"]
14
+ * @returns {import("./types.js").SiteGenerator}
15
+ */
16
+ static newSiteGenerator(config, outputDir, options = {}, type = "11ty") {
17
+ switch (type) {
18
+ case "jekyll":
19
+ return new Jekyll(config, outputDir);
20
+ }
21
+ return new EleventyGenerator(config, outputDir, options);
22
+ }
23
+
24
+ /**
25
+ * @param {import("./types.js").SiteGenerator} siteGenerator
10
26
  * @param {import("./types.js").Taxa} taxa
11
27
  * @param {import("./types.js").TaxaColDef[]} [familyCols]
12
28
  */
13
- static renderBasePages(outputDir, taxa, familyCols) {
14
- const siteGenerator = new Jekyll(outputDir);
29
+ static renderBasePages(siteGenerator, taxa, familyCols) {
30
+ const outputDir = siteGenerator.getBaseDir();
15
31
 
16
32
  // Copy static files
17
- // First copy default Jekyll files from package.
18
- Files.copyDir(Config.getPackageDir() + "/jekyll", outputDir);
19
- // Then copy Jekyll files from current dir (which may override default files).
20
- Files.copyDir("jekyll", outputDir);
33
+ siteGenerator.copyStaticFiles();
34
+
35
+ // Copy static files
36
+ siteGenerator.copyGeneratorFiles();
21
37
 
22
38
  // Copy illustrations.
23
39
  siteGenerator.copyIllustrations(taxa.getFlowerColors());
24
40
 
25
41
  const fl = new PageFamilyList(
26
- outputDir,
42
+ siteGenerator,
27
43
  taxa.getFamilies().getFamilies(),
28
44
  );
29
45
  fl.render(familyCols);
30
- fl.renderPages(outputDir, familyCols);
46
+ fl.renderPages(siteGenerator, familyCols);
31
47
 
32
48
  new GlossaryPages(siteGenerator).renderPages();
33
49
 
@@ -41,22 +57,26 @@ export class BasePageRenderer {
41
57
  static renderTools(outputDir, taxa) {
42
58
  const names = [];
43
59
  for (const taxon of taxa.getTaxonList()) {
44
- const row = [];
45
- row.push(taxon.getName());
60
+ /** @type {(import("./types.js").NameSearchData)} */
61
+ const row = { t: taxon.getName() };
46
62
  const cn = taxon.getCommonNames().join(", ");
47
63
  if (cn) {
48
- row.push(cn);
64
+ row.c = cn;
49
65
  }
50
66
  const synonyms = [];
51
67
  for (const syn of taxon.getSynonyms()) {
52
68
  synonyms.push(syn);
53
69
  }
54
70
  if (synonyms.length > 0) {
55
- row[2] = synonyms;
71
+ row.s = synonyms;
56
72
  }
57
73
  names.push(row);
58
74
  }
59
75
 
60
- Files.write(outputDir + "/_includes/names.json", JSON.stringify(names));
76
+ Files.write(
77
+ path.join(outputDir, "./assets/js/nameSearchData.js"),
78
+ `export const NAMES = ${JSON.stringify(names)};`,
79
+ true,
80
+ );
61
81
  }
62
82
  }
package/lib/config.js CHANGED
@@ -8,7 +8,7 @@ const COUNTY_NAMES = {
8
8
  CCA: "Contra Costa",
9
9
  };
10
10
 
11
- class Config {
11
+ export class Config {
12
12
  static #packageDir = path.dirname(
13
13
  path.dirname(url.fileURLToPath(import.meta.url)),
14
14
  );
@@ -88,6 +88,8 @@ class Config {
88
88
  static getPackageDir() {
89
89
  return this.#packageDir;
90
90
  }
91
- }
92
91
 
93
- export { Config };
92
+ getSiteName() {
93
+ return this.#config.siteName ?? "California Plants";
94
+ }
95
+ }
@@ -1,12 +1,11 @@
1
1
  import * as fs from "node:fs";
2
2
 
3
- class EBookPage {
3
+ export class EBookPage {
4
4
  #fileName;
5
5
  #title;
6
6
  #rootPrefix;
7
7
 
8
8
  /**
9
- *
10
9
  * @param {string} fileName
11
10
  * @param {string} title
12
11
  * @param {string} rootPrefix
@@ -56,5 +55,3 @@ class EBookPage {
56
55
  return html;
57
56
  }
58
57
  }
59
-
60
- export { EBookPage };
@@ -1,14 +1,8 @@
1
+ import path from "node:path";
1
2
  import { Files } from "../files.js";
2
3
  import { SiteGenerator } from "../sitegenerator.js";
3
4
 
4
- class EBookSiteGenerator extends SiteGenerator {
5
- /**
6
- * @param {string} baseDir
7
- */
8
- constructor(baseDir) {
9
- super(baseDir);
10
- }
11
-
5
+ export class EBookSiteGenerator extends SiteGenerator {
12
6
  #pageEnd() {
13
7
  return "</body></html>";
14
8
  }
@@ -47,10 +41,8 @@ class EBookSiteGenerator extends SiteGenerator {
47
41
  writeTemplate(content, attributes, filename) {
48
42
  const depth = (filename.match(/\//g) || []).length;
49
43
  Files.write(
50
- Files.join(this.getBaseDir(), filename),
51
- this.#wrap(depth, content, attributes)
44
+ path.join(this.getBaseDir(), filename),
45
+ this.#wrap(depth, content, attributes),
52
46
  );
53
47
  }
54
48
  }
55
-
56
- export { EBookSiteGenerator };
@@ -1,7 +1,7 @@
1
1
  import { GlossaryPages as BaseGlossaryPages } from "../web/glossarypages.js";
2
2
  import { EBook } from "./ebook.js";
3
3
 
4
- class GlossaryPages extends BaseGlossaryPages {
4
+ export class GlossaryPages extends BaseGlossaryPages {
5
5
  /**
6
6
  * @param {import("../sitegenerator.js").SiteGenerator} siteGenerator
7
7
  */
@@ -39,5 +39,3 @@ class GlossaryPages extends BaseGlossaryPages {
39
39
  return spineEntries.join("");
40
40
  }
41
41
  }
42
-
43
- export { GlossaryPages };
@@ -29,7 +29,7 @@ class PlantBook extends EBook {
29
29
  );
30
30
 
31
31
  this.#taxa = taxa;
32
- const generator = new EBookSiteGenerator(this.getContentDir());
32
+ const generator = new EBookSiteGenerator(config, this.getContentDir());
33
33
  this.#glossary = new GlossaryPages(generator);
34
34
  this.#images = new Images(generator, this.getContentDir(), taxa);
35
35
  }
package/lib/files.js CHANGED
@@ -83,6 +83,7 @@ class Files {
83
83
  /**
84
84
  * @param {string[]} paths
85
85
  * @returns {string}
86
+ * @deprecated
86
87
  */
87
88
  static join(...paths) {
88
89
  return path.join(...paths).replaceAll("\\", "/");
package/lib/htmltaxon.js CHANGED
@@ -1,10 +1,11 @@
1
+ import path from "node:path";
1
2
  import { Config } from "./config.js";
2
3
  import { DateUtils } from "./dateutils.js";
3
4
  import { ExternalSites } from "./externalsites.js";
4
5
  import { HTML } from "./html.js";
5
- import { Markdown } from "./markdown.js";
6
6
  import { RarePlants } from "./rareplants.js";
7
7
  import { TextUtils } from "./textutils.js";
8
+ import { HTMLFragments } from "./utils/htmlFragments.js";
8
9
 
9
10
  /**
10
11
  * @type {Record<string,import("./types.js").TaxaColDef>}
@@ -204,12 +205,12 @@ class HTMLTaxon {
204
205
  * @returns {string}
205
206
  */
206
207
  static getFooterHTML(taxon) {
207
- const footerTextPath =
208
- Config.getPackageDir() +
209
- "/data/text/" +
210
- taxon.getBaseFileName() +
211
- ".footer.md";
212
- return HTMLTaxon.getMarkdownSection(footerTextPath);
208
+ const footerTextPath = path.join(
209
+ Config.getPackageDir(),
210
+ "/data/text/",
211
+ `${taxon.getBaseFileName()}.footer.md`,
212
+ );
213
+ return HTMLFragments.getMarkdownSection(footerTextPath);
213
214
  }
214
215
 
215
216
  /**
@@ -271,18 +272,6 @@ class HTMLTaxon {
271
272
  return html;
272
273
  }
273
274
 
274
- /**
275
- * @param {string} filePath
276
- * @returns {string}
277
- */
278
- static getMarkdownSection(filePath) {
279
- const footerMarkdown = Markdown.fileToHTML(filePath);
280
- if (footerMarkdown) {
281
- return HTML.wrap("div", footerMarkdown, "section");
282
- }
283
- return "";
284
- }
285
-
286
275
  /**
287
276
  * @param {import("./types.js").Taxon} taxon
288
277
  */
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 };