hdoc-tools 0.45.1 → 0.47.0

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.
package/hdoc-build.js CHANGED
@@ -1,7 +1,6 @@
1
1
  (() => {
2
2
  const crypto = require("node:crypto");
3
- const dree = require("dree");
4
- const fs = require("fs-extra");
3
+ const fs = require("node:fs");
5
4
  const mdfm = require("markdown-it-front-matter");
6
5
  const path = require("node:path");
7
6
  const puppeteer = require("puppeteer");
@@ -11,10 +10,8 @@
11
10
  const hdoc_build_pdf = require(path.join(__dirname, "hdoc-build-pdf.js"));
12
11
  const hdoc_index = require(path.join(__dirname, "hdoc-db.js"));
13
12
  const archiver = require("archiver");
14
- const xmlFormat = require("xml-formatter");
15
13
 
16
14
  const { execSync } = require("child_process");
17
- const tmp = require("tmp");
18
15
 
19
16
  const h_tags_to_search = ["h1", "h2", "h3"];
20
17
  const image_extensions = ["png", "svg", "jpg"];
@@ -101,6 +98,71 @@
101
98
  let mermaid_images_path = "";
102
99
  let verbose = false;
103
100
 
101
+ // Mutable references updated immediately before each md.render() call so
102
+ // the highlight and frontmatter plugin callbacks can identify the current
103
+ // file. Safe because md.render() is synchronous — nothing can interleave
104
+ // between setting these and reading them back within a single render.
105
+ let currentMdFilePath = "";
106
+ let currentFrontmatter = "";
107
+
108
+ // Shared markdown-it instance — created once, reused for every file.
109
+ // Previously recreated per file because the highlight callback closed over
110
+ // the file_path parameter; it now reads currentMdFilePath instead.
111
+ // mdfm and tips are registered once here rather than once per file.
112
+ const md = require("markdown-it")({
113
+ html: true,
114
+ linkify: true,
115
+ typographer: true,
116
+ highlight: function (str, lang) {
117
+ if (lang === "mermaid" && process.env.GITHUB_ACTIONS !== 'true') {
118
+ try {
119
+ const tmpInput = hdoc.tmp_file_sync({ postfix: ".mmd" });
120
+ const outputFileName = `mermaid-${crypto.createHash("sha256").update(str).digest("hex").slice(0, 16)}.svg`;
121
+ const outputPath = path.join(mermaid_images_path, outputFileName);
122
+ const outputLink = `/_books/${doc_id}/mermaid-images/${outputFileName}`;
123
+
124
+ if (!str.startsWith('---')) {
125
+ str = '---\n' + fs.readFileSync(mermaid_theme_path, {encoding: 'utf-8'}) + `\n---\n${str}`;
126
+ }
127
+
128
+ fs.writeFileSync(tmpInput.name, str);
129
+ let cmd = `${__dirname}/node_modules/.bin/mmdc`;
130
+
131
+ if (process.platform === "win32") {
132
+ cmd = `"${cmd}.cmd"`;
133
+ }
134
+
135
+ cmd = `${cmd} -i "${tmpInput.name}" -o "${outputPath}" --backgroundColor transparent --puppeteerConfigFile ${mermaid_puppeteer_config_path}`;
136
+ console.log(`Generating Mermaid SVG found in ${currentMdFilePath.relativePath} - ${outputPath}`);
137
+ execSync(cmd);
138
+
139
+ if (!fs.existsSync(outputPath)) {
140
+ throw new Error("mmdc did not generate output");
141
+ }
142
+
143
+ tmpInput.removeCallback();
144
+
145
+ return `<img class="mermaid-diagram" src="${outputLink}" alt="Mermaid Diagram">`;
146
+ } catch (err) {
147
+ mermaid_failures.push({path: currentMdFilePath.relativePath, error: err.message});
148
+ return ``;
149
+ }
150
+ }
151
+ }
152
+ });
153
+ md.linkify.set({
154
+ fuzzyEmail: false,
155
+ fuzzyLink: false,
156
+ fuzzyIP: false,
157
+ });
158
+ // mdfm callback writes to currentFrontmatter, which is reset to "" before
159
+ // each render so every file gets a clean slate.
160
+ md.use(mdfm, (fm) => {
161
+ currentFrontmatter = fm;
162
+ });
163
+ const tips = require(`${__dirname}/custom_modules/tips.js`);
164
+ md.use(tips, { links: true });
165
+
104
166
  const pdf_path_excluded = (relative_path) => {
105
167
  if (
106
168
  !hdocbook_project.pdfGeneration ||
@@ -128,62 +190,202 @@
128
190
  return false;
129
191
  };
130
192
 
131
- const transform_static_html = async (file_path) => {
132
- if (fs.existsSync(file_path.path)) {
193
+ // Processes a single documentation file (markdown or static HTML), generates
194
+ // frontmatter headers, retrieves contributor data from GitHub, optionally
195
+ // generates a PDF, and writes the final HTML to disk ready for indexing.
196
+ // Markdown files are rendered to HTML first; static HTML files are processed
197
+ // in place. Both paths share the same post-processing pipeline.
198
+ const transform_file = async (file_path) => {
199
+ const is_markdown = path.extname(file_path.path) === '.md';
200
+ if (is_markdown) conversion_attempted++;
201
+
202
+ if (!fs.existsSync(file_path.path)) {
203
+ if (is_markdown) {
204
+ conversion_failed++;
205
+ console.error("MD file does not exist:", file_path.path);
206
+ return false;
207
+ }
208
+ return;
209
+ }
210
+
211
+ let html_txt;
212
+ const fm_headers = [];
213
+ let doc_title = "";
214
+ let doc_type = "Article";
215
+ let fm_status = false;
216
+ // Used only for static HTML: tracks whether the source file already had a
217
+ // frontmatter comment block that needs replacing in the output.
218
+ let existing_fm_headers = false;
219
+ let html_fm = null;
220
+
221
+ if (is_markdown) {
222
+ // Load markdown file
223
+ let md_txt = hdoc.expand_variables(
224
+ fs.readFileSync(file_path.path, "utf8"),
225
+ );
226
+
227
+ // Pull in external includes
228
+ const includes_processed = await hdoc.process_includes(
229
+ file_path.path,
230
+ md_txt,
231
+ global_source_path,
232
+ );
233
+ md_txt = includes_processed.body.toString();
234
+ includes_found += includes_processed.found;
235
+ includes_success += includes_processed.success;
236
+ includes_failed += includes_processed.failed;
237
+ if (includes_processed.errors.length > 0) {
238
+ for (let i = 0; i < includes_processed.errors.length; i++) {
239
+ console.error(includes_processed.errors[i]);
240
+ }
241
+ }
242
+
243
+ // Point the shared md instance at the current file before rendering
244
+ // so the highlight callback logs and reports against the right path.
245
+ currentMdFilePath = file_path;
246
+ currentFrontmatter = "";
247
+
248
+ // Tidy up ```json and ```xml code tags
249
+ if (md_txt.includes("```json") || md_txt.includes("```xml"))
250
+ md_txt = tidy_code_tags(md_txt, file_path.relativePath);
251
+
252
+ // Render markdown into HTML
253
+ html_txt = md.render(md_txt);
254
+
255
+ // Parse frontmatter properties from the YAML block
256
+ let fm_contains_title = false;
257
+ let fm_contains_reading_time = false;
258
+ let fm_contains_description = false;
259
+
260
+ const fm_content = currentFrontmatter.split(/\r?\n/);
261
+ if (fm_content.length >= 0) {
262
+ for (fm_prop of fm_content) {
263
+ const fm_id = fm_prop.slice(0, fm_prop.indexOf(":"));
264
+ const fm_val = fm_prop.slice(fm_prop.indexOf(":") + 1);
265
+
266
+ if (
267
+ fm_id &&
268
+ fm_id.trim().length > 0 &&
269
+ fm_val &&
270
+ fm_val.trim().length > 0
271
+ ) {
272
+ fm_headers.push({
273
+ id: fm_id.trim(),
274
+ value: fm_val.trim(),
275
+ });
276
+
277
+ if (fm_id.trim() === "title") {
278
+ fm_contains_title = true;
279
+ doc_title = fm_val.trim();
280
+ }
281
+ if (fm_id.trim() === "status") {
282
+ fm_status = true;
283
+ }
284
+ if (fm_id.trim() === "type") {
285
+ doc_type = fm_val.trim();
286
+ }
287
+ if (fm_id.trim() === "reading-time") {
288
+ book_read_time += Number.parseInt(fm_val.trim(), 10);
289
+ fm_contains_reading_time = true;
290
+ }
291
+ if (fm_id.trim() === "description") {
292
+ fm_contains_description = true;
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ // Title from heading if not in frontmatter
299
+ if (!fm_contains_title) {
300
+ const html_heading = hdoc.getFirstHTMLHeading(
301
+ html_txt,
302
+ h_tags_to_search,
303
+ );
304
+
305
+ if (html_heading?.[0]?.children?.[0]?.data) {
306
+ fm_headers.push({
307
+ id: "title",
308
+ value: html_heading[0].children[0].data.trim(),
309
+ });
310
+ doc_title = html_heading[0].children[0].data.trim();
311
+ } else if (
312
+ file_path.name !== "description_ext.md" &&
313
+ file_path.name !== "article_ext.md" &&
314
+ file_path.name !== "internal_ext.md"
315
+ ) {
316
+ console.info(
317
+ `[WARNING] No frontmatter title property, or ${h_tags_to_search.join(", ")} tags detected in ${file_path.path}`,
318
+ );
319
+ }
320
+ }
321
+
322
+ // Description from first paragraph if not in frontmatter
323
+ if (!fm_contains_description) {
324
+ const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
325
+ if (html_p_tag?.[0]?.children?.[0]?.data) {
326
+ fm_headers.push({
327
+ id: "description",
328
+ value:
329
+ `${doc_title}: ${html_p_tag[0].children[0].data.split(".")[0]}.`.trim(),
330
+ });
331
+ }
332
+ }
333
+
334
+ // Reading time from content if not in frontmatter
335
+ if (!fm_contains_reading_time) {
336
+ const read_time_mins = hdoc.get_html_read_time(html_txt);
337
+ book_read_time += read_time_mins;
338
+ fm_headers.push({
339
+ id: "reading-time",
340
+ value: read_time_mins,
341
+ });
342
+ }
343
+ } else {
133
344
  // Load HTML file
134
- let html_txt = fs.readFileSync(file_path.path, "utf8");
345
+ html_txt = fs.readFileSync(file_path.path, "utf8");
135
346
  html_txt = html_txt.replace(/\r/gm, ""); // Remove CR's so we're just dealing with newlines
136
347
 
137
- const fm_headers = [];
138
- let existing_fm_headers = false;
139
- let doc_type = "Article";
140
- let doc_title = "";
141
-
142
348
  // Check if we have a frontmatter comment
143
- const fm_header = hdoc.getHTMLFrontmatterHeader(html_txt);
349
+ html_fm = hdoc.getHTMLFrontmatterHeader(html_txt);
144
350
 
145
- let rel_path = file_path.relativePath.replace(path.extname(file_path.relativePath), '');
146
- if (rel_path.endsWith('/index')) rel_path = rel_path.substring(0, rel_path.length - 6);
147
- const is_draft = draft_links.indexOf(rel_path) !== -1;
148
-
149
- let fm_status = false;
150
- if (Object.keys(fm_header.fm_properties).length > 0) {
351
+ if (Object.keys(html_fm.fm_properties).length > 0) {
151
352
  existing_fm_headers = true;
152
353
 
153
- if (fm_header.fm_properties?.status) {
354
+ if (html_fm.fm_properties?.status) {
154
355
  fm_status = true;
155
356
  }
357
+
156
358
  // We have some frontmatter headers, check if title is one of them
157
359
  let fm_title_found = false;
158
360
  if (
159
- fm_header.fm_properties &&
160
- fm_header.fm_properties.title !== undefined
361
+ html_fm.fm_properties &&
362
+ html_fm.fm_properties.title !== undefined
161
363
  ) {
162
364
  // We have a title - but does the title have a value
163
- if (fm_header.fm_properties.title === "") {
164
- // No value - remove title from the properties map so we don't end up with 2 title properties, one empty and one with a value
165
- fm_header.fm_properties.title = undefined;
365
+ if (html_fm.fm_properties.title === "") {
366
+ // No value - remove title from the properties map so we don't end up with 2 title properties, one empty and one with a value
367
+ html_fm.fm_properties.title = undefined;
166
368
  } else {
167
369
  // We have a value for the title property
168
370
  fm_title_found = true;
169
- doc_title = fm_header.fm_properties.title.trim();
371
+ doc_title = html_fm.fm_properties.title.trim();
170
372
  }
171
373
  }
172
374
 
173
375
  // Is reading-time in the fm headers?
174
- if (fm_header.fm_properties["reading-time"] === undefined) {
376
+ if (html_fm.fm_properties["reading-time"] === undefined) {
175
377
  const read_time_mins = hdoc.get_html_read_time(html_txt);
176
378
  book_read_time += read_time_mins;
177
- fm_header.fm_properties["reading-time"] = read_time_mins;
379
+ html_fm.fm_properties["reading-time"] = read_time_mins;
178
380
  }
179
381
 
180
- for (const key in fm_header.fm_properties) {
181
- if (Object.hasOwn(fm_header.fm_properties, key)) {
182
- if (key === "type") doc_type = fm_header.fm_properties[key];
382
+ for (const key in html_fm.fm_properties) {
383
+ if (Object.hasOwn(html_fm.fm_properties, key)) {
384
+ if (key === "type") doc_type = html_fm.fm_properties[key];
183
385
  else {
184
386
  fm_headers.push({
185
387
  id: key,
186
- value: fm_header.fm_properties[key],
388
+ value: html_fm.fm_properties[key],
187
389
  });
188
390
  }
189
391
  }
@@ -192,10 +394,10 @@
192
394
  if (
193
395
  !fm_title_found &&
194
396
  file_path.name !== "description_ext.md" &&
195
- file_path.name !== "article_ext.md"
397
+ file_path.name !== "article_ext.md" &&
398
+ file_path.name !== "internal_ext.md"
196
399
  ) {
197
- // No frontmatter title found in properties
198
- // Go get title from h tags in html
400
+ // No frontmatter title found in properties - go get title from h tags in html
199
401
  const html_heading = hdoc.getFirstHTMLHeading(
200
402
  html_txt,
201
403
  h_tags_to_search,
@@ -220,10 +422,10 @@
220
422
 
221
423
  // Do we have a description header?
222
424
  if (
223
- fm_header.fm_properties &&
224
- fm_header.fm_properties.description !== undefined
425
+ html_fm.fm_properties &&
426
+ html_fm.fm_properties.description !== undefined
225
427
  ) {
226
- if (fm_header.fm_properties.description === "") {
428
+ if (html_fm.fm_properties.description === "") {
227
429
  const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
228
430
  if (html_p_tag?.[0]?.children?.[0]?.data) {
229
431
  fm_headers.push({
@@ -235,7 +437,7 @@
235
437
  } else {
236
438
  fm_headers.push({
237
439
  id: "description",
238
- value: fm_header.fm_properties.description.trim(),
440
+ value: html_fm.fm_properties.description.trim(),
239
441
  });
240
442
  }
241
443
  } else {
@@ -254,20 +456,20 @@
254
456
  html_txt,
255
457
  h_tags_to_search,
256
458
  );
257
- let doc_title = "";
459
+ let doc_title_local = "";
258
460
  // Add the title
259
461
  if (html_heading?.[0]?.children?.[0]?.data) {
260
- // We've found a heading tag, add that as a title to the frontmatter content
261
462
  fm_headers.push({
262
463
  id: "title",
263
464
  value: html_heading[0].children[0].data,
264
465
  });
265
- doc_title = html_heading[0].children[0].data;
466
+ doc_title_local = html_heading[0].children[0].data;
467
+ doc_title = doc_title_local;
266
468
  } else if (
267
469
  file_path.name !== "description_ext.md" &&
268
- file_path.name !== "article_ext.md"
470
+ file_path.name !== "article_ext.md" &&
471
+ file_path.name !== "internal_ext.md"
269
472
  ) {
270
- // No header tag, no frontmatter title, output a warning
271
473
  console.info(
272
474
  `[WARNING] No frontmatter title property, or ${h_tags_to_search.join(
273
475
  ", ",
@@ -288,633 +490,244 @@
288
490
  fm_headers.push({
289
491
  id: "description",
290
492
  value:
291
- `${doc_title}: ${html_p_tag[0].children[0].data.split(".")[0]}.`.trim(),
292
- });
293
- }
294
- }
295
-
296
- // Add doc type
297
- fm_headers.push({
298
- id: "type",
299
- value: doc_type,
300
- });
301
-
302
- // Add status if we don't have one defined and the nav tree defines the document as draft
303
- if (!fm_status && is_draft) {
304
- fm_headers.push({
305
- id: "status",
306
- value: "draft"
307
- });
308
- }
309
-
310
- const metadata = {};
311
-
312
- // Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
313
- let html_h1 = h1_pattern.exec(html_txt);
314
- if (html_h1?.[0])
315
- html_h1 = html_h1[0].replace(/(<h1.*?>)/, "").replace(/(<\/h1>)/, "");
316
-
317
- html_txt = html_txt.replace(h1_pattern, "");
318
-
319
- // Get contributor data from Github, if exists
320
- let contribs = [];
321
- let last_commit = null;
322
- if (
323
- hdocbook_config.publicSource &&
324
- hdocbook_config.publicSource !== "" &&
325
- hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
326
- ) {
327
- const github_paths = hdoc.get_github_api_path(
328
- hdocbook_config.publicSource,
329
- file_path.relativePath,
330
- );
331
-
332
- const contributors = await hdoc.get_github_contributors(
333
- github_paths.api_path,
334
- git_token,
335
- );
336
-
337
- if (!contributors.success) {
338
- console.error(
339
- `Error retrieving contributors from Github: ${contributors.error}`,
340
- );
341
- } else {
342
- last_commit = contributors.last_commit_date;
343
- metadata.last_commit = contributors.last_commit_date;
344
- metadata.contributor_count = contributors.contributor_count;
345
- metadata.edit_url = github_paths.edit_path;
346
- contribs = contributors.contributors;
347
- contributors.editPath = github_paths.edit_path;
348
- fm_headers.push({
349
- id: "contributor-count",
350
- value: contributors.contributor_count,
493
+ `${doc_title_local}: ${html_p_tag[0].children[0].data.split(".")[0]}.`.trim(),
351
494
  });
352
- fm_headers.push({
353
- id: "last-commit",
354
- value: contributors.last_commit_date,
355
- });
356
- const target_file = file_path.path.replace(
357
- path.extname(file_path.path),
358
- "._info.json",
359
- );
360
- contributors.success = undefined;
361
- contributors.error = undefined;
362
- contributors.editPath = github_paths.edit_path;
363
- }
364
- fm_headers.push({
365
- id: "edit-path",
366
- value: github_paths.edit_path,
367
- });
368
- }
369
-
370
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
371
- fm_headers.push({
372
- id: "pdf-path",
373
- value: file_path.relativePath.replace(
374
- path.extname(file_path.relativePath),
375
- ".pdf",
376
- ),
377
- });
378
- }
379
-
380
- let fm_header_content = "<!--[[FRONTMATTER\n";
381
- if (fm_headers.length > 0) {
382
- for (let i = 0; i < fm_headers.length; i++) {
383
- fm_header_content += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
384
- }
385
- fm_header_content += "]]-->";
386
-
387
- if (existing_fm_headers) {
388
- html_txt = html_txt.replace(`<!--${fm_header.fm_header}-->`, "");
389
495
  }
390
496
  }
391
-
392
- let doc_header = "";
393
- let pdf_header = "";
394
- const inline_content = file_path.relativePath.startsWith(
395
- `${hdocbook_config.docId}/_inline/`,
396
- );
397
- if (hdocbook_config.publicSource?.includes("github.com/Hornbill-Docs")) {
398
- // Build doc header from template and frontmatter tags
399
- if (!inline_content)
400
- doc_header = process_doc_header(
401
- fm_headers,
402
- file_path.relativePath,
403
- doc_header_template,
404
- html_h1,
405
- );
406
-
407
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
408
- pdf_header = process_doc_header(
409
- fm_headers,
410
- file_path.relativePath,
411
- pdf_header_template,
412
- html_h1,
413
- );
414
- } else {
415
- if (!inline_content)
416
- doc_header = process_doc_header(
417
- fm_headers,
418
- file_path.relativePath,
419
- doc_header_template_non_git,
420
- html_h1,
421
- );
422
-
423
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
424
- pdf_header = process_doc_header(
425
- fm_headers,
426
- file_path.relativePath,
427
- pdf_header_template_non_git,
428
- html_h1,
429
- );
430
- }
431
-
432
- let pdf_size = 0;
433
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
434
- let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
435
- pdf_txt = `${pdf_header}\n${pdf_txt}`;
436
-
437
- // Generate PDF file from HTML
438
- const pdf_file_path = file_path.path.replace(
439
- path.extname(file_path.path),
440
- ".pdf",
441
- );
442
- pdf_size = await hdoc_build_pdf.generate_pdf(
443
- browser,
444
- pdf_template_path,
445
- pdf_template,
446
- hdocbook_config,
447
- pdf_txt,
448
- pdf_file_path,
449
- css_templates,
450
- verbose,
451
- );
452
- }
453
- if (pdf_size > 0) pdf_created++;
454
-
455
- // Wrap h2 and h3 tags, plus content, in id'd divs
456
- html_txt = hdoc.wrapHContent(html_txt);
457
-
458
- if (inline_content) html_txt = `${fm_header_content}\n${html_txt}`;
459
- else html_txt = `${fm_header_content}\n${doc_header}\n${html_txt}`;
460
-
461
- let relative_path = file_path.relativePath;
462
- if (
463
- !bc[relative_path.replace(".html", "")] &&
464
- bc[relative_path.replace("/index.html", "")]
465
- ) {
466
- relative_path = relative_path.replace("/index.html", "");
467
- }
468
-
469
- const index_data = hdoc_index.transform_html_for_index(html_txt);
470
-
471
- for (const section of index_data.sections) {
472
- index_records.push({
473
- relative_path: relative_path,
474
- index_html: {
475
- fm_props: index_data.fm_props,
476
- text: section.text,
477
- preview: section.preview,
478
- id: section.id ? section.id : null,
479
- },
480
- metadata: metadata,
481
- contributors: contribs,
482
- pdf_size: pdf_size,
483
- md5: file_path.hash,
484
- lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
485
- inline: inline_content,
486
- status: index_data.fm_props.status ? index_data.fm_props.status : 'release',
487
- keywords: index_data.fm_props.keywords ? index_data.fm_props.keywords : '',
488
- });
489
- }
490
-
491
- // Save HTML into HTML file
492
- try {
493
- fs.writeFileSync(file_path.path, html_txt);
494
- } catch (err) {
495
- console.error("Error writing:", target_file, "\n", err);
496
- }
497
497
  }
498
- };
499
-
500
- const transform_markdown_and_save_html = async (file_path) => {
501
- conversion_attempted++;
502
-
503
- if (fs.existsSync(file_path.path)) {
504
- // Load markdown file
505
- let md_txt = hdoc.expand_variables(
506
- fs.readFileSync(file_path.path, "utf8"),
507
- );
508
498
 
509
- // Pull in external includes
510
- const includes_processed = await hdoc.process_includes(
511
- file_path.path,
512
- md_txt,
513
- global_source_path,
514
- );
515
- md_txt = includes_processed.body.toString();
516
- includes_found += includes_processed.found;
517
- includes_success += includes_processed.success;
518
- includes_failed += includes_processed.failed;
519
- if (includes_processed.errors.length > 0) {
520
- for (let i = 0; i < includes_processed.errors.length; i++) {
521
- console.error(includes_processed.errors[i]);
522
- }
523
- }
499
+ // ── Shared post-processing ─────────────────────────────────────────────
524
500
 
525
- // One markdown parser per file. Seems wrong, but doesn't work with a global one once past the first md file
526
- // Steve - revisit this
527
- const md = require("markdown-it")({
528
- html: true,
529
- linkify: true,
530
- typographer: true,
531
- highlight: function (str, lang) {
532
-
533
- if (lang === "mermaid" && process.env.GITHUB_ACTIONS !== 'true') {
534
- try {
535
- const tmpInput = tmp.fileSync({ postfix: ".mmd" });
536
- const outputFileName = `mermaid-${crypto.createHash("sha256").update(str).digest("hex").slice(0, 16)}.svg`;
537
- const outputPath = path.join(mermaid_images_path, outputFileName);
538
- const outputLink = `/_books/${doc_id}/mermaid-images/${outputFileName}`
539
-
540
- if (!str.startsWith('---')) {
541
- str = '---\n' + fs.readFileSync(mermaid_theme_path, {encoding: 'utf-8'}) + `\n---\n${str}`;
542
- }
543
-
544
- fs.writeFileSync(tmpInput.name, str);
545
- let cmd = `${__dirname}/node_modules/.bin/mmdc`;
546
-
547
- if (process.platform === "win32") {
548
- cmd = `"${cmd}.cmd"`;
549
- }
550
-
551
-
552
- cmd = `${cmd} -i "${tmpInput.name}" -o "${outputPath}" --backgroundColor transparent --puppeteerConfigFile ${mermaid_puppeteer_config_path}`;
553
- console.log(`Generating Mermaid SVG found in ${file_path.relativePath} - ${outputPath}`);
554
- execSync(cmd);
555
-
556
- if (!fs.existsSync(outputPath)) {
557
- throw new Error("mmdc did not generate output");
558
- }
559
-
560
- tmpInput.removeCallback();
561
-
562
- return `<img class="mermaid-diagram" src="${outputLink}" alt="Mermaid Diagram">`;
563
- } catch (err) {
564
- mermaid_failures.push({path: file_path.relativePath, error: err.message});
565
- return ``;
566
- }
567
- }
568
- }
569
- });
570
- md.linkify.set({
571
- fuzzyEmail: false,
572
- fuzzyLink: false,
573
- fuzzyIP: false,
574
- });
501
+ let rel_path = file_path.relativePath.replace(path.extname(file_path.relativePath), '');
502
+ if (rel_path.endsWith('/index')) rel_path = rel_path.substring(0, rel_path.length - 6);
503
+ const is_draft = draft_links.indexOf(rel_path) !== -1;
575
504
 
576
- // Process Frontmatter tags
577
- let frontmatter_content = "";
578
- md.use(mdfm, (fm) => {
579
- frontmatter_content = fm;
580
- });
505
+ // Add doc type
506
+ fm_headers.push({
507
+ id: "type",
508
+ value: doc_type,
509
+ });
581
510
 
582
- // Process tips
583
- const tips = require(`${__dirname}/custom_modules/tips.js`);
584
- md.use(tips, {
585
- links: true,
511
+ // Add status if we don't have one defined and the nav tree defines the document as draft
512
+ if (!fm_status && is_draft) {
513
+ fm_headers.push({
514
+ id: "status",
515
+ value: "draft"
586
516
  });
517
+ }
587
518
 
588
- // Tidy up ```json and ```xml code tags
589
- if (md_txt.includes("```json") || md_txt.includes("```xml"))
590
- md_txt = tidy_code_tags(md_txt, file_path.relativePath);
591
-
592
- // Render markdown into HTML
593
- let html_txt = md.render(md_txt);
594
-
595
- // Prepare frontmatter headers
596
- const fm_headers = [];
597
- const fm_content = frontmatter_content.split(/\r?\n/);
598
-
599
- let fm_contains_title = false;
600
- let fm_contains_reading_time = false;
601
- let fm_contains_description = false;
602
- let doc_title = "";
603
- let doc_type = "Article";
604
-
605
- let rel_path = file_path.relativePath.replace(path.extname(file_path.relativePath), '');
606
- if (rel_path.endsWith('/index')) rel_path = rel_path.substring(0, rel_path.length - 6);
607
- const is_draft = draft_links.indexOf(rel_path) !== -1;
608
- let fm_status = false;
519
+ const metadata = {};
609
520
 
610
- if (fm_content.length >= 0) {
611
- for (fm_prop of fm_content) {
612
- const fm_id = fm_prop.slice(0, fm_prop.indexOf(":"));
613
- const fm_val = fm_prop.slice(fm_prop.indexOf(":") + 1);
521
+ // Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
522
+ let html_h1 = h1_pattern.exec(html_txt);
523
+ if (html_h1?.[0])
524
+ html_h1 = html_h1[0].replace(/(<h1.*?>)/, "").replace(/(<\/h1>)/, "");
614
525
 
615
- if (
616
- fm_id &&
617
- fm_id.trim().length > 0 &&
618
- fm_val &&
619
- fm_val.trim().length > 0
620
- ) {
621
- fm_headers.push({
622
- id: fm_id.trim(),
623
- value: fm_val.trim(),
624
- });
526
+ html_txt = html_txt.replace(h1_pattern, "");
625
527
 
626
- if (fm_id.trim() === "title") {
627
- fm_contains_title = true;
628
- doc_title = fm_val.trim();
629
- }
630
- if (fm_id.trim() === "status") {
631
- fm_status = true;
632
- }
633
- if (fm_id.trim() === "type") {
634
- doc_type = fm_val.trim();
635
- }
636
- if (fm_id.trim() === "reading-time") {
637
- book_read_time += Number.parseInt(fm_val.trim(), 10);
638
- fm_contains_reading_time = true;
639
- }
640
- if (fm_id.trim() === "description") {
641
- fm_contains_description = true;
642
- }
643
- }
644
- }
645
- }
528
+ // Get contributor data from Github, if exists
529
+ let contribs = [];
530
+ let last_commit = null;
531
+ if (
532
+ hdocbook_config.publicSource &&
533
+ hdocbook_config.publicSource !== "" &&
534
+ hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
535
+ ) {
536
+ const github_paths = hdoc.get_github_api_path(
537
+ hdocbook_config.publicSource,
538
+ file_path.relativePath,
539
+ );
540
+ const contributors = await hdoc.get_github_contributors(
541
+ github_paths.api_path,
542
+ git_token,
543
+ is_markdown ? hdocbook_config.publicSource : undefined,
544
+ );
646
545
 
647
- // Add doc type
648
- fm_headers.push({
649
- id: "type",
650
- value: doc_type,
651
- });
546
+ if (!contributors.success) {
547
+ console.error(
548
+ `Error retrieving contributors from Github: ${contributors.error}`,
549
+ );
550
+ } else {
551
+ last_commit = contributors.last_commit_date;
552
+ metadata.last_commit = contributors.last_commit_date;
553
+ metadata.contributor_count = contributors.contributor_count;
554
+ metadata.edit_url = github_paths.edit_path;
555
+ contribs = contributors.contributors;
556
+ contributors.editPath = github_paths.edit_path;
652
557
 
653
- // Add status if we don't have one defined and the nav tree defines the document as draft
654
- if (!fm_status && is_draft) {
655
558
  fm_headers.push({
656
- id: "status",
657
- value: "draft"
559
+ id: "contributor-count",
560
+ value: contributors.contributor_count,
658
561
  });
659
- }
660
-
661
- // Does frontmatter tag contain a title property
662
- if (!fm_contains_title) {
663
- // Frontmatter tags don't contain a title property - go pull the first one from the html heading tags
664
- const html_heading = hdoc.getFirstHTMLHeading(
665
- html_txt,
666
- h_tags_to_search,
667
- );
668
-
669
- if (html_heading?.[0]?.children?.[0]?.data) {
670
- // We've found a heading tag, add that as a title to the frontmatter content
671
- fm_headers.push({
672
- id: "title",
673
- value: html_heading[0].children[0].data.trim(),
674
- });
675
- doc_title = html_heading[0].children[0].data.trim();
676
- } else if (
677
- file_path.name !== "description_ext.md" &&
678
- file_path.name !== "article_ext.md"
679
- ) {
680
- // No header tag, no frontmatter title, output a warning
681
- console.info(
682
- `[WARNING] No frontmatter title property, or h1, h2 or h3 header tags detected in ${file_path.path}`,
683
- );
684
- }
685
- }
686
-
687
- // Does frontmatter contain a description header, generate one if not
688
- if (!fm_contains_description) {
689
- const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
690
- if (html_p_tag?.[0]?.children?.[0]?.data) {
691
- fm_headers.push({
692
- id: "description",
693
- value:
694
- `${doc_title}: ${html_p_tag[0].children[0].data.split(".")[0]}.`.trim(),
695
- });
696
- }
697
- }
698
-
699
- // Does frontmatter tag contain a reading-time property
700
- if (!fm_contains_reading_time) {
701
- const read_time_mins = hdoc.get_html_read_time(html_txt);
702
- book_read_time += read_time_mins;
703
562
  fm_headers.push({
704
- id: "reading-time",
705
- value: read_time_mins,
563
+ id: "last-commit",
564
+ value: contributors.last_commit_date,
706
565
  });
707
- }
708
- const metadata = {};
709
-
710
- // Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
711
- let html_h1 = h1_pattern.exec(html_txt);
712
- if (html_h1?.[0])
713
- html_h1 = html_h1[0].replace(/(<h1.*?>)/, "").replace(/(<\/h1>)/, "");
714
-
715
- html_txt = html_txt.replace(h1_pattern, "");
716
-
717
- // Get contributor data from Github, if exists
718
- let contribs = [];
719
- let last_commit = null;
720
- if (
721
- hdocbook_config.publicSource &&
722
- hdocbook_config.publicSource !== "" &&
723
- hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
724
- ) {
725
- const github_paths = hdoc.get_github_api_path(
726
- hdocbook_config.publicSource,
727
- file_path.relativePath,
728
- );
729
- const contributors = await hdoc.get_github_contributors(
730
- github_paths.api_path,
731
- git_token,
732
- hdocbook_config.publicSource,
733
- );
566
+ contributors.success = undefined;
567
+ contributors.error = undefined;
568
+ contributors.editPath = github_paths.edit_path;
734
569
 
735
- if (!contributors.success) {
736
- console.error(
737
- `Error retrieving contributors from Github: ${contributors.error}`,
738
- );
739
- } else {
740
- last_commit = contributors.last_commit_date;
741
- metadata.last_commit = contributors.last_commit_date;
742
- metadata.contributor_count = contributors.contributor_count;
743
- metadata.edit_url = github_paths.edit_path;
744
- contribs = contributors.contributors;
745
- contributors.editPath = github_paths.edit_path;
746
-
747
- fm_headers.push({
748
- id: "contributor-count",
749
- value: contributors.contributor_count,
750
- });
751
- fm_headers.push({
752
- id: "last-commit",
753
- value: contributors.last_commit_date,
754
- });
755
- const target_file = file_path.path.replace(
570
+ if (is_markdown) {
571
+ // Write contributor metadata to a sidecar JSON file for later use
572
+ const target_info_file = file_path.path.replace(
756
573
  path.extname(file_path.path),
757
574
  "._info.json",
758
575
  );
759
- contributors.success = undefined;
760
- contributors.error = undefined;
761
- contributors.editPath = github_paths.edit_path;
762
576
  try {
763
577
  fs.writeFileSync(
764
- target_file,
578
+ target_info_file,
765
579
  JSON.stringify(contributors, null, 2),
766
580
  );
767
581
  } catch (err) {
768
- console.error("Error writing:", target_file, "\n", err);
582
+ console.error("Error writing:", target_info_file, "\n", err);
769
583
  }
770
584
  }
771
- fm_headers.push({
772
- id: "edit-path",
773
- value: github_paths.edit_path,
774
- });
775
585
  }
586
+ fm_headers.push({
587
+ id: "edit-path",
588
+ value: github_paths.edit_path,
589
+ });
590
+ }
776
591
 
777
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
778
- fm_headers.push({
779
- id: "pdf-path",
780
- value: file_path.relativePath.replace(
781
- path.extname(file_path.relativePath),
782
- ".pdf",
783
- ),
784
- });
785
- }
592
+ if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
593
+ fm_headers.push({
594
+ id: "pdf-path",
595
+ value: file_path.relativePath.replace(
596
+ path.extname(file_path.relativePath),
597
+ ".pdf",
598
+ ),
599
+ });
600
+ }
786
601
 
787
- // Add frontmatter tags as comment
788
- let fm_header = "<!--[[FRONTMATTER\n";
789
- if (fm_headers.length > 0) {
790
- for (let i = 0; i < fm_headers.length; i++) {
791
- fm_header += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
792
- }
602
+ // Build frontmatter comment block
603
+ let fm_header_str = "<!--[[FRONTMATTER\n";
604
+ if (fm_headers.length > 0) {
605
+ for (let i = 0; i < fm_headers.length; i++) {
606
+ fm_header_str += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
793
607
  }
794
- fm_header += "]]-->";
795
-
796
- let doc_header = "";
797
- let pdf_header = "";
798
- const inline_content = file_path.relativePath.startsWith(
799
- `${hdocbook_config.docId}/_inline/`,
800
- );
801
- if (hdocbook_config.publicSource?.includes("github.com/Hornbill-Docs")) {
802
- // Build doc header from template and frontmatter tags
803
- if (!inline_content)
804
- doc_header = process_doc_header(
805
- fm_headers,
806
- file_path.relativePath,
807
- doc_header_template,
808
- html_h1,
809
- );
810
-
811
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
812
- pdf_header = process_doc_header(
813
- fm_headers,
814
- file_path.relativePath,
815
- pdf_header_template,
816
- html_h1,
817
- );
818
- } else {
819
- // Build doc header from template and frontmatter tags
820
- if (!inline_content)
821
- doc_header = process_doc_header(
822
- fm_headers,
823
- file_path.relativePath,
824
- doc_header_template_non_git,
825
- html_h1,
826
- );
827
-
828
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
829
- pdf_header = process_doc_header(
830
- fm_headers,
831
- file_path.relativePath,
832
- pdf_header_template_non_git,
833
- html_h1,
834
- );
608
+ // For static HTML, strip the original frontmatter comment before prepending the new one
609
+ if (!is_markdown && existing_fm_headers) {
610
+ html_txt = html_txt.replace(`<!--${html_fm.fm_header}-->`, "");
835
611
  }
612
+ }
613
+ fm_header_str += "]]-->";
836
614
 
837
- let pdf_size = 0;
838
- if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
839
- let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
840
- pdf_txt = `${pdf_header}\n${pdf_txt}`;
615
+ let doc_header = "";
616
+ let pdf_header = "";
617
+ const inline_content = file_path.relativePath.startsWith(
618
+ `${hdocbook_config.docId}/_inline/`,
619
+ );
620
+ if (hdocbook_config.publicSource?.includes("github.com/Hornbill-Docs")) {
621
+ // Build doc header from template and frontmatter tags
622
+ if (!inline_content)
623
+ doc_header = process_doc_header(
624
+ fm_headers,
625
+ file_path.relativePath,
626
+ doc_header_template,
627
+ html_h1,
628
+ );
841
629
 
842
- // Generate PDF file from HTML
843
- const pdf_file_path = file_path.path.replace(
844
- path.extname(file_path.path),
845
- ".pdf",
630
+ if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
631
+ pdf_header = process_doc_header(
632
+ fm_headers,
633
+ file_path.relativePath,
634
+ pdf_header_template,
635
+ html_h1,
846
636
  );
847
- pdf_size = await hdoc_build_pdf.generate_pdf(
848
- browser,
849
- pdf_template_path,
850
- pdf_template,
851
- hdocbook_config,
852
- pdf_txt,
853
- pdf_file_path,
854
- css_templates,
855
- verbose,
637
+ } else {
638
+ if (!inline_content)
639
+ doc_header = process_doc_header(
640
+ fm_headers,
641
+ file_path.relativePath,
642
+ doc_header_template_non_git,
643
+ html_h1,
856
644
  );
857
- }
858
- if (pdf_size > 0) pdf_created++;
859
645
 
860
- // Wrap h2 and h3 tags, plus content, in id'd divs
861
- html_txt = hdoc.wrapHContent(html_txt);
646
+ if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
647
+ pdf_header = process_doc_header(
648
+ fm_headers,
649
+ file_path.relativePath,
650
+ pdf_header_template_non_git,
651
+ html_h1,
652
+ );
653
+ }
862
654
 
863
- if (inline_content) html_txt = `${fm_header}\n${html_txt}`;
864
- else html_txt = `${fm_header}\n${doc_header}\n${html_txt}`;
655
+ let pdf_size = 0;
656
+ if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
657
+ let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
658
+ pdf_txt = `${pdf_header}\n${pdf_txt}`;
865
659
 
866
- // Save HTML into HTML file
867
- const target_file = file_path.path.replace(
660
+ // Generate PDF file from HTML
661
+ const pdf_file_path = file_path.path.replace(
868
662
  path.extname(file_path.path),
869
- ".html",
663
+ ".pdf",
870
664
  );
871
- let relative_path = file_path.relativePath.replace(
872
- path.extname(file_path.path),
873
- ".html",
665
+ pdf_size = await hdoc_build_pdf.generate_pdf(
666
+ browser,
667
+ pdf_template_path,
668
+ pdf_template,
669
+ hdocbook_config,
670
+ pdf_txt,
671
+ pdf_file_path,
672
+ css_templates,
673
+ verbose,
874
674
  );
875
- try {
876
- fs.writeFileSync(target_file, html_txt);
877
- } catch (err) {
878
- console.error("Error writing:", target_file, "\n", err);
879
- }
675
+ }
676
+ if (pdf_size > 0) pdf_created++;
880
677
 
881
- if (
882
- !bc[relative_path.replace(".html", "")] &&
883
- bc[relative_path.replace("/index.html", "")]
884
- ) {
885
- relative_path = relative_path.replace("/index.html", "");
886
- }
678
+ // Wrap h2 and h3 tags, plus content, in id'd divs
679
+ html_txt = hdoc.wrapHContent(html_txt);
887
680
 
888
- const index_data = hdoc_index.transform_html_for_index(html_txt);
889
- for (section of index_data.sections) {
890
- index_records.push({
891
- relative_path: relative_path,
892
- index_html: {
893
- fm_props: index_data.fm_props,
894
- text: section.text,
895
- preview: section.preview,
896
- id: section.id ? section.id : null,
897
- },
898
- metadata: metadata,
899
- contributors: contribs,
900
- pdf_size: pdf_size,
901
- md5: file_path.hash,
902
- lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
903
- inline: inline_content,
904
- status: index_data.fm_props.status ? index_data.fm_props.status : 'release',
905
- keywords: index_data.fm_props.keywords ? index_data.fm_props.keywords : '',
906
- });
907
- }
681
+ if (inline_content) html_txt = `${fm_header_str}\n${html_txt}`;
682
+ else html_txt = `${fm_header_str}\n${doc_header}\n${html_txt}`;
908
683
 
684
+ // Determine output file path (.md → .html for markdown; same path for static HTML)
685
+ const target_file = is_markdown
686
+ ? file_path.path.replace(path.extname(file_path.path), ".html")
687
+ : file_path.path;
688
+ try {
689
+ fs.writeFileSync(target_file, html_txt);
690
+ } catch (err) {
691
+ console.error("Error writing:", target_file, "\n", err);
692
+ }
693
+
694
+ let relative_path = is_markdown
695
+ ? file_path.relativePath.replace(path.extname(file_path.path), ".html")
696
+ : file_path.relativePath;
697
+ if (
698
+ !bc[relative_path.replace(".html", "")] &&
699
+ bc[relative_path.replace("/index.html", "")]
700
+ ) {
701
+ relative_path = relative_path.replace("/index.html", "");
702
+ }
703
+
704
+ const index_data = hdoc_index.transform_html_for_index(html_txt);
705
+ for (const section of index_data.sections) {
706
+ index_records.push({
707
+ relative_path: relative_path,
708
+ index_html: {
709
+ fm_props: index_data.fm_props,
710
+ text: section.text,
711
+ preview: section.preview,
712
+ id: section.id ? section.id : null,
713
+ },
714
+ metadata: metadata,
715
+ contributors: contribs,
716
+ pdf_size: pdf_size,
717
+ md5: file_path.hash,
718
+ lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
719
+ inline: inline_content,
720
+ status: index_data.fm_props.status ? index_data.fm_props.status : 'release',
721
+ keywords: index_data.fm_props.keywords ? index_data.fm_props.keywords : '',
722
+ });
723
+ }
724
+
725
+ if (is_markdown) {
909
726
  // Add MD file to delete queue
910
727
  md_files_delete.push(file_path.path);
911
-
912
728
  conversion_success++;
913
729
  return true;
914
730
  }
915
- conversion_failed++;
916
- console.error("MD file does not exist:", file_path.path);
917
- return false;
918
731
  };
919
732
 
920
733
  const tidy_code_tags = (markdown, file) => {
@@ -950,7 +763,7 @@
950
763
  .replace("```", "");
951
764
  let new_xml_string = xml_tidy;
952
765
  try {
953
- new_xml_string = xmlFormat(xml_tidy, {
766
+ new_xml_string = hdoc.xml_format(xml_tidy, {
954
767
  indentation: " ",
955
768
  collapseContent: true,
956
769
  lineSeparator: "\n",
@@ -1105,7 +918,7 @@
1105
918
  const filename_validation_callback = (element) => {
1106
919
  if (element.relativePath.startsWith("_inline/")) return;
1107
920
  if (element.name.toLowerCase() === ".ds_store") return;
1108
- if (element.name === "article_ext.md" || element.name === "description_ext.md" ) return;
921
+ if (element.name === "article_ext.md" || element.name === "description_ext.md" || element.name === "internal_ext.md" ) return;
1109
922
 
1110
923
  const file_no_ext = element.name.replace(`.${element.extension}`, "");
1111
924
 
@@ -1290,7 +1103,7 @@
1290
1103
 
1291
1104
  // Validate all filenames first
1292
1105
  console.log("Validating book filenames meet kebab-case requirements...");
1293
- dree.scan(book_path, dreeOptionsAllFiles, filename_validation_callback);
1106
+ hdoc.scan_dir(book_path, dreeOptionsAllFiles, filename_validation_callback);
1294
1107
  if (errors_filename.length > 0) {
1295
1108
  console.log("\r\n-----------------------");
1296
1109
  console.log(" Validation Output ");
@@ -1322,7 +1135,8 @@
1322
1135
  // Copy files from book into _work-doc_id folder
1323
1136
  console.log("Copying content into work folder...");
1324
1137
  try {
1325
- fs.copySync(path.join(source_path, doc_id), work_path_content, {
1138
+ fs.cpSync(path.join(source_path, doc_id), work_path_content, {
1139
+ recursive: true,
1326
1140
  filter: file_filter,
1327
1141
  });
1328
1142
  } catch (e) {
@@ -1336,7 +1150,7 @@
1336
1150
  // Create MD5 hash of content before build
1337
1151
  console.log("Creating Hash...");
1338
1152
 
1339
- dree.scan(work_path_content, md5DreeOptions, hash_callback);
1153
+ hdoc.scan_dir(work_path_content, md5DreeOptions, hash_callback);
1340
1154
  let concat_hash = "|";
1341
1155
  for (let i = 0; i < built_file_hashes.length; i++) {
1342
1156
  concat_hash += `${built_file_hashes[i].path}:${built_file_hashes[i].hash}|`;
@@ -1405,7 +1219,7 @@
1405
1219
  console.log("Processing content...");
1406
1220
 
1407
1221
  // Get a list of MD files in work_path
1408
- dree.scan(work_path, dreeOptions, build_file_callback);
1222
+ hdoc.scan_dir(work_path, dreeOptions, build_file_callback);
1409
1223
 
1410
1224
  // Create a Chromium browser instance generate PDFs and validate links with
1411
1225
  browser = await puppeteer.launch({ headless: "shell", args: ['--no-sandbox'] });
@@ -1421,7 +1235,7 @@
1421
1235
  // do whatever
1422
1236
  await Promise.all(
1423
1237
  chunk.map(async (file) => {
1424
- await transform_markdown_and_save_html(file);
1238
+ await transform_file(file);
1425
1239
  }),
1426
1240
  );
1427
1241
  }
@@ -1435,7 +1249,7 @@
1435
1249
  const chunk = htmlPromiseArray.slice(i, i + chunkSize);
1436
1250
  await Promise.all(
1437
1251
  chunk.map(async (file) => {
1438
- await transform_static_html(file);
1252
+ await transform_file(file);
1439
1253
  }),
1440
1254
  );
1441
1255
  }