hdoc-tools 0.9.17 → 0.9.19
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/README.md +5 -3
- package/hdoc-build-pdf.js +2 -8
- package/hdoc-build.js +36 -4
- package/hdoc-module.js +16 -2
- package/hdoc-validate.js +73 -46
- package/hdoc.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
@@ -41,13 +41,15 @@ If the -v switch is provided, then more verbose output is output, which includes
|
|
41
41
|
|
42
42
|
Performs a local build of the book, validates the links and static content are present and correct and outputs as a ZIP file.
|
43
43
|
|
44
|
-
If the -v switch is provided, then more verbose output is provided, which includes a list of all HTML files created and checked for embedded links and static content.
|
44
|
+
If the -v switch is provided, then a more verbose output is provided, which includes a list of all HTML files created and checked for embedded links and static content.
|
45
45
|
|
46
|
-
|
46
|
+
Use the --set-version argument to set the version number of the built book.
|
47
|
+
|
48
|
+
### validate
|
47
49
|
|
48
50
|
Performs a minimum local build of the book, then validates the links and static content are present and correct.
|
49
51
|
|
50
|
-
If the -v switch is provided, then more verbose output is provided, which includes a list of all HTML files created and checked for embedded links and static content.
|
52
|
+
If the -v switch is provided, then a more verbose output is provided, which includes a list of all HTML files created and checked for embedded links and static content.
|
51
53
|
|
52
54
|
Use the --set-version argument to set the version number of the built book.
|
53
55
|
|
package/hdoc-build-pdf.js
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
fs = require('fs-extra'),
|
8
8
|
mime = require('mime-types'),
|
9
9
|
path = require('path'),
|
10
|
-
puppeteer = require('puppeteer'),
|
11
10
|
hdoc = require(path.join(__dirname, 'hdoc-module.js'));
|
12
11
|
|
13
12
|
const dree_options = {
|
@@ -99,7 +98,7 @@
|
|
99
98
|
return html_source;
|
100
99
|
};
|
101
100
|
|
102
|
-
exports.generate_pdf = async function (pdf_template_path, pdf_template_content, book_config, html_source, target_file, verbose = false) {
|
101
|
+
exports.generate_pdf = async function (browser, pdf_template_path, pdf_template_content, book_config, html_source, target_file, verbose = false) {
|
103
102
|
if (verbose) console.log(`Generating PDF: ${target_file}`);
|
104
103
|
let pdf_size = 0;
|
105
104
|
// Cache footer
|
@@ -120,9 +119,6 @@
|
|
120
119
|
|
121
120
|
html_source = pdf_template_content.replace('{{book_title}}', book_config.title).replace('{{document_content}}', html_source).replace('{{hb_logo}}', hb_logo);
|
122
121
|
|
123
|
-
// Create a browser instance
|
124
|
-
const browser = await puppeteer.launch();
|
125
|
-
|
126
122
|
// Create a new page
|
127
123
|
const page = await browser.newPage();
|
128
124
|
|
@@ -166,9 +162,7 @@
|
|
166
162
|
} catch (err) {
|
167
163
|
console.log(`Error generating PDF ${target_file} - ${err}`);
|
168
164
|
}
|
169
|
-
|
170
|
-
// Close the browser instance
|
171
|
-
await browser.close();
|
165
|
+
await page.close();
|
172
166
|
return pdf_size;
|
173
167
|
};
|
174
168
|
})();
|
package/hdoc-build.js
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
fs = require('fs-extra'),
|
7
7
|
mdfm = require('markdown-it-front-matter'),
|
8
8
|
path = require('path'),
|
9
|
+
puppeteer = require('puppeteer'),
|
9
10
|
URL = require("url").URL,
|
10
11
|
hdoc_validate = require(path.join(__dirname, 'hdoc-validate.js')),
|
11
12
|
hdoc = require(path.join(__dirname, 'hdoc-module.js')),
|
@@ -26,8 +27,9 @@
|
|
26
27
|
regex_version = /^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,6}$/;
|
27
28
|
|
28
29
|
let bc = {}, // Breadcrumbs map
|
29
|
-
book_read_time = 0,
|
30
30
|
built_file_hashes = [],
|
31
|
+
book_read_time = 0,
|
32
|
+
browser = {},
|
31
33
|
conversion_attempted = 0,
|
32
34
|
conversion_success = 0,
|
33
35
|
conversion_failed = 0,
|
@@ -238,7 +240,7 @@
|
|
238
240
|
|
239
241
|
// Generate PDF file from HTML
|
240
242
|
const pdf_file_path = file_path.path.replace(path.extname(file_path.path), '.pdf');
|
241
|
-
pdf_size = await hdoc_build_pdf.generate_pdf(pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, verbose);
|
243
|
+
pdf_size = await hdoc_build_pdf.generate_pdf(browser, pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, verbose);
|
242
244
|
}
|
243
245
|
|
244
246
|
html_txt = `${fm_header_content}\n${doc_header}\n${html_txt}`;
|
@@ -448,7 +450,7 @@
|
|
448
450
|
|
449
451
|
// Generate PDF file from HTML
|
450
452
|
const pdf_file_path = file_path.path.replace(path.extname(file_path.path), '.pdf');
|
451
|
-
pdf_size = await hdoc_build_pdf.generate_pdf(pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, verbose);
|
453
|
+
pdf_size = await hdoc_build_pdf.generate_pdf(browser, pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, verbose);
|
452
454
|
}
|
453
455
|
|
454
456
|
html_txt = `${fm_header}\n${doc_header}\n${html_txt}`;
|
@@ -572,6 +574,7 @@
|
|
572
574
|
}
|
573
575
|
verbose = verbose_output;
|
574
576
|
|
577
|
+
const start_time = Date.now();
|
575
578
|
// GERRY: The purpose of this function is to create a zip file containing the hdocbook content,
|
576
579
|
// * Create a _work folder
|
577
580
|
// * copy the hdocbook content to the work folder
|
@@ -688,6 +691,11 @@
|
|
688
691
|
// Get a list of MD files in work_path
|
689
692
|
dree.scan(work_path, dreeOptions, build_file_callback);
|
690
693
|
|
694
|
+
if (pdf_enable) {
|
695
|
+
// Create a Chromium browser instance to generate PDFs with
|
696
|
+
browser = await puppeteer.launch({headless: 'new'});
|
697
|
+
}
|
698
|
+
|
691
699
|
// Work through MD files and convert to HTML
|
692
700
|
for (let i = 0; i < md_files.length; i++) {
|
693
701
|
await transform_markdown_and_save_html(md_files[i]);
|
@@ -698,6 +706,11 @@
|
|
698
706
|
await transform_static_html(static_html_files[i]);
|
699
707
|
}
|
700
708
|
|
709
|
+
if (pdf_enable) {
|
710
|
+
// Close the Chromium browser instance
|
711
|
+
await browser.close();
|
712
|
+
}
|
713
|
+
|
701
714
|
// Output to console
|
702
715
|
console.log(` MD files found: ${conversion_attempted}`);
|
703
716
|
console.log(`Successfully converted to HTML: ${conversion_success}`);
|
@@ -710,6 +723,8 @@
|
|
710
723
|
// Validate content
|
711
724
|
const validation_success = await hdoc_validate.run(work_path, doc_id, verbose, hdocbook_config, hdocbook_project, bc);
|
712
725
|
if (!validation_success) {
|
726
|
+
const end_time = Date.now();
|
727
|
+
console.log(`\nTime Taken: ${get_duration(start_time, end_time)}\n`);
|
713
728
|
process.exit(1);
|
714
729
|
}
|
715
730
|
|
@@ -722,9 +737,10 @@
|
|
722
737
|
console.log(`Error deleting ${md_files_delete[i]}: ${e}`);
|
723
738
|
}
|
724
739
|
}
|
725
|
-
|
740
|
+
|
726
741
|
// Add book read timing to the hdocbook.json
|
727
742
|
hdocbook_config.readingTime = Math.ceil(book_read_time + ((book_read_time / 100) * 10));
|
743
|
+
hdocbook_config.navigation.items = hdoc.strip_drafts(hdocbook_config.navigation.items);
|
728
744
|
try {
|
729
745
|
fs.writeFileSync(work_hdocbook_path, JSON.stringify(hdocbook_config, null, 2));
|
730
746
|
console.log('\nhdocbook.json update success:', work_hdocbook_path);
|
@@ -761,5 +777,21 @@
|
|
761
777
|
} else {
|
762
778
|
console.log('\nValidation Complete\n');
|
763
779
|
}
|
780
|
+
const end_time = Date.now();
|
781
|
+
console.log(`Time Taken: ${get_duration(start_time, end_time)}\n`);
|
764
782
|
};
|
783
|
+
|
784
|
+
const get_duration = function(start, end) {
|
785
|
+
const total_time = new Date(end - start).toISOString().slice(11,19);
|
786
|
+
const duration_arr = total_time.split(':');
|
787
|
+
let duration = '';
|
788
|
+
if (parseInt(duration_arr[0], 10) > 0) {
|
789
|
+
duration += parseInt(duration_arr[0], 10) + 'h ';
|
790
|
+
}
|
791
|
+
if (duration !== '' || parseInt(duration_arr[1], 10)) {
|
792
|
+
duration += parseInt(duration_arr[1], 10) + 'm ';
|
793
|
+
}
|
794
|
+
duration += parseInt(duration_arr[2], 10) + 's';
|
795
|
+
return duration;
|
796
|
+
}
|
765
797
|
})();
|
package/hdoc-module.js
CHANGED
@@ -290,7 +290,22 @@
|
|
290
290
|
return response;
|
291
291
|
};
|
292
292
|
|
293
|
-
|
293
|
+
exports.strip_drafts = function (nav_items) {
|
294
|
+
let return_nav = nav_items;
|
295
|
+
recurse_nav(return_nav);
|
296
|
+
return return_nav;
|
297
|
+
};
|
298
|
+
const recurse_nav = function(nav_items) {
|
299
|
+
for (const key in nav_items) {
|
300
|
+
if (nav_items[key].draft) {
|
301
|
+
nav_items.splice(key, 1);
|
302
|
+
recurse_nav(nav_items);
|
303
|
+
} else if (nav_items[key].items) {
|
304
|
+
recurse_nav(nav_items[key].items);
|
305
|
+
}
|
306
|
+
}
|
307
|
+
};
|
308
|
+
|
294
309
|
exports.build_breadcrumbs = function (nav_items) {
|
295
310
|
let bc = {};
|
296
311
|
// Steve - need to rework this to use recursion, as it's hard-coded for a maximum of 4 levels deep right now
|
@@ -379,5 +394,4 @@
|
|
379
394
|
}
|
380
395
|
return bc;
|
381
396
|
};
|
382
|
-
|
383
397
|
})();
|
package/hdoc-validate.js
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
const {
|
2
|
+
file
|
3
|
+
} = require('jszip');
|
4
|
+
|
1
5
|
(function () {
|
2
6
|
'use strict';
|
3
7
|
|
@@ -8,17 +12,18 @@
|
|
8
12
|
https = require('https'),
|
9
13
|
path = require('path'),
|
10
14
|
hdoc = require(path.join(__dirname, 'hdoc-module.js')),
|
11
|
-
translator = require('american-british-english-translator')
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
translator = require('american-british-english-translator'),
|
16
|
+
{ trueCasePathSync } = require('true-case-path');
|
17
|
+
|
18
|
+
const agent = new https.Agent({
|
19
|
+
rejectUnauthorized: false
|
20
|
+
}),
|
21
|
+
spellcheck_options = {
|
22
|
+
british: true,
|
23
|
+
spelling: true
|
24
|
+
},
|
25
|
+
regex_nav_paths = /[a-z0-9-\/]+/g;
|
26
|
+
|
22
27
|
let prod_families = {},
|
23
28
|
prods_supported = [];
|
24
29
|
|
@@ -31,9 +36,11 @@
|
|
31
36
|
exclude_links = {},
|
32
37
|
exclude_spellcheck = {};
|
33
38
|
|
34
|
-
const load_product_families = async function() {
|
39
|
+
const load_product_families = async function () {
|
35
40
|
try {
|
36
|
-
const prods = await axios.get('https://docs.hornbill.com/_books/products.json', {
|
41
|
+
const prods = await axios.get('https://docs.hornbill.com/_books/products.json', {
|
42
|
+
httpsAgent: agent
|
43
|
+
});
|
37
44
|
if (prods.status === 200) {
|
38
45
|
prod_families = prods.data;
|
39
46
|
for (let i = 0; i < prod_families.products.length; i++) {
|
@@ -51,9 +58,9 @@
|
|
51
58
|
|
52
59
|
const spellcheckContent = async function (sourceFile, excludes) {
|
53
60
|
const text = fs.readFileSync(sourceFile.path, 'utf8');
|
54
|
-
const source_path = sourceFile.relativePath.replace('.'+sourceFile.extension, '');
|
61
|
+
const source_path = sourceFile.relativePath.replace('.' + sourceFile.extension, '');
|
55
62
|
const translate_output = translator.translate(text, spellcheck_options);
|
56
|
-
if(Object.keys(translate_output).length){
|
63
|
+
if (Object.keys(translate_output).length) {
|
57
64
|
|
58
65
|
for (const key in translate_output) {
|
59
66
|
if (translate_output.hasOwnProperty(key)) {
|
@@ -70,11 +77,11 @@
|
|
70
77
|
}
|
71
78
|
}
|
72
79
|
}
|
73
|
-
}
|
80
|
+
}
|
74
81
|
}
|
75
82
|
};
|
76
83
|
|
77
|
-
const checkNavigation = async function (
|
84
|
+
const checkNavigation = async function (source_path, flat_nav) {
|
78
85
|
let nav_errors = [];
|
79
86
|
for (const key in flat_nav) {
|
80
87
|
if (flat_nav.hasOwnProperty(key)) {
|
@@ -82,15 +89,33 @@
|
|
82
89
|
const invalid_chars = key.replace(regex_nav_paths, '');
|
83
90
|
if (invalid_chars !== '') {
|
84
91
|
nav_errors.push(`Navigation path [${key}] contains the following invalid characters: [${[...invalid_chars].join('] [')}]`);
|
85
|
-
}
|
86
|
-
|
87
|
-
//Validate path
|
92
|
+
}
|
93
|
+
|
94
|
+
// Validate path exists - key should be a html file at this point
|
95
|
+
let file_exists = true;
|
96
|
+
let file_name = path.join(source_path, key + '.html');
|
97
|
+
if (!fs.existsSync(file_name)) {
|
98
|
+
file_name = path.join(source_path, key + '.htm');
|
99
|
+
if (!fs.existsSync(file_name)) {
|
100
|
+
file_exists = false;
|
101
|
+
nav_errors.push(`Navigation path [${key}] file does not exist.`);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
if (file_exists) {
|
106
|
+
const true_file = trueCasePathSync(file_name);
|
107
|
+
if (true_file !== file_name) {
|
108
|
+
nav_errors.push(`Navigation path [${key}] does not match filename case [${path.basename(true_file)}].`);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
// Validate path
|
88
113
|
const paths = key.split('/');
|
89
114
|
for (let i = 0; i < paths.length; i++) {
|
90
115
|
const path_words = paths[i].split('-');
|
91
116
|
for (let j = 0; j < path_words.length; j++) {
|
92
117
|
const translate_output = translator.translate(path_words[j], spellcheck_options);
|
93
|
-
if(Object.keys(translate_output).length){
|
118
|
+
if (Object.keys(translate_output).length) {
|
94
119
|
for (const spell_val in translate_output) {
|
95
120
|
if (translate_output.hasOwnProperty(spell_val)) {
|
96
121
|
for (const spelling in translate_output[spell_val][0]) {
|
@@ -108,7 +133,7 @@
|
|
108
133
|
for (let i = 0; i < flat_nav[key].length; i++) {
|
109
134
|
if (flat_nav[key][i].link === key) {
|
110
135
|
const translate_output = translator.translate(flat_nav[key][i].text, spellcheck_options);
|
111
|
-
if(Object.keys(translate_output).length){
|
136
|
+
if (Object.keys(translate_output).length) {
|
112
137
|
for (const spell_val in translate_output) {
|
113
138
|
if (translate_output.hasOwnProperty(spell_val)) {
|
114
139
|
for (let j = 0; j < translate_output[spell_val].length; j++) {
|
@@ -163,7 +188,9 @@
|
|
163
188
|
}
|
164
189
|
|
165
190
|
try {
|
166
|
-
await axios.get(links[i], {
|
191
|
+
await axios.get(links[i], {
|
192
|
+
httpsAgent: agent
|
193
|
+
});
|
167
194
|
messages[htmlFile.relativePath].push(`Link is a valid external URL: ${links[i]}`);
|
168
195
|
} catch (e) {
|
169
196
|
// Handle errors
|
@@ -197,15 +224,17 @@
|
|
197
224
|
const checkTags = async function (htmlFile) {
|
198
225
|
const htmlBody = fs.readFileSync(htmlFile.path, 'utf8');
|
199
226
|
const $ = cheerio.load(htmlBody);
|
200
|
-
|
227
|
+
|
201
228
|
const h1_tags = $('h1').map(function () {
|
202
229
|
return $(this);
|
203
230
|
}).get();
|
231
|
+
|
204
232
|
if (h1_tags.length && h1_tags.length > 1) {
|
233
|
+
//console.log("PATH: ", htmlFile.path); //hdoc-guide/hdocbook/markdown.html
|
205
234
|
let error_msg = `${h1_tags.length} <h1> tags found in content: `;
|
206
235
|
for (let i = 0; i < h1_tags.length; i++) {
|
207
|
-
error_msg
|
208
|
-
if (i <
|
236
|
+
error_msg += h1_tags[i].text();
|
237
|
+
if (i < h1_tags.length - 1) error_msg += '; ';
|
209
238
|
}
|
210
239
|
errors[htmlFile.relativePath].push(error_msg);
|
211
240
|
}
|
@@ -214,7 +243,7 @@
|
|
214
243
|
const dreeOptions = {
|
215
244
|
descendants: true,
|
216
245
|
depth: 10,
|
217
|
-
extensions: ['htm', 'html','md'],
|
246
|
+
extensions: ['htm', 'html', 'md'],
|
218
247
|
hash: false,
|
219
248
|
normalize: true,
|
220
249
|
size: false,
|
@@ -324,7 +353,7 @@
|
|
324
353
|
for (let i = 0; i < prod_families.products.length; i++) {
|
325
354
|
if (prod_families.products[i].id === hdocbook_config.productFamily) {
|
326
355
|
valid_product = true;
|
327
|
-
}
|
356
|
+
}
|
328
357
|
}
|
329
358
|
if (!valid_product) {
|
330
359
|
let val_prod_error = `Incorrect productFamily: ${hdocbook_config.productFamily}. Supported values:`;
|
@@ -332,18 +361,17 @@
|
|
332
361
|
val_prod_error += `\n - ${prods_supported[i]}`
|
333
362
|
}
|
334
363
|
meta_errors.push(val_prod_error)
|
335
|
-
|
364
|
+
|
336
365
|
}
|
337
366
|
|
338
367
|
// Check navigation spellings
|
339
|
-
const nav_errors = await checkNavigation(
|
368
|
+
const nav_errors = await checkNavigation(source_path, nav_items);
|
340
369
|
if (nav_errors.length > 0) meta_errors.push(...nav_errors);
|
341
370
|
|
342
371
|
if (meta_errors.length > 0) {
|
343
372
|
console.log('\r\n-----------------------');
|
344
373
|
console.log(' Validation Output ');
|
345
374
|
console.log('-----------------------');
|
346
|
-
console.log()
|
347
375
|
console.log(`\r\n${meta_errors.length} Validation Errors Found\r\n`);
|
348
376
|
for (let i = 0; i < meta_errors.length; i++) {
|
349
377
|
console.log(`- ${meta_errors[i]}`);
|
@@ -353,12 +381,12 @@
|
|
353
381
|
|
354
382
|
if (hdocbook_project.validation) {
|
355
383
|
if (hdocbook_project.validation.exclude_links && hdocbook_project.validation.exclude_links instanceof Array) {
|
356
|
-
hdocbook_project.validation.exclude_links.forEach(function(excl_link) {
|
357
|
-
|
358
|
-
|
384
|
+
hdocbook_project.validation.exclude_links.forEach(function (excl_link) {
|
385
|
+
exclude_links[excl_link] = true;
|
386
|
+
});
|
359
387
|
}
|
360
388
|
if (hdocbook_project.validation.exclude_spellcheck && hdocbook_project.validation.exclude_spellcheck instanceof Array) {
|
361
|
-
hdocbook_project.validation.exclude_spellcheck.forEach(function(excl_sc) {
|
389
|
+
hdocbook_project.validation.exclude_spellcheck.forEach(function (excl_sc) {
|
362
390
|
exclude_spellcheck[excl_sc.document_path] = excl_sc.words;
|
363
391
|
});
|
364
392
|
}
|
@@ -437,21 +465,20 @@
|
|
437
465
|
console.log('\r\n-----------------------');
|
438
466
|
console.log(' Validation Output ');
|
439
467
|
console.log('-----------------------');
|
440
|
-
|
441
|
-
for (const key in errors) {
|
442
|
-
if (errors.hasOwnProperty(key) && errors[key].length > 0) {
|
443
|
-
console.log(`\r\n${errors[key].length} error(s) in ${key}`);
|
444
|
-
for (let i = 0; i < errors[key].length; i++) {
|
445
|
-
console.log(` - ${errors[key][i]}`);
|
446
|
-
errorcount++;
|
447
|
-
}
|
448
|
-
}
|
449
|
-
}
|
450
|
-
|
451
468
|
if (errorcount > 0) {
|
452
469
|
console.log(`\r\n${errorcount} Validation Errors Found\r\n`);
|
470
|
+
for (const key in errors) {
|
471
|
+
if (errors.hasOwnProperty(key) && errors[key].length > 0) {
|
472
|
+
console.log(`\r\n${errors[key].length} error(s) in ${key}`);
|
473
|
+
for (let i = 0; i < errors[key].length; i++) {
|
474
|
+
console.log(` - ${errors[key][i]}`);
|
475
|
+
errorcount++;
|
476
|
+
}
|
477
|
+
}
|
478
|
+
}
|
453
479
|
return false;
|
454
480
|
}
|
481
|
+
|
455
482
|
console.log(`\r\nNo Validation Errors Found!\r\n`);
|
456
483
|
return true;
|
457
484
|
};
|
package/hdoc.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "hdoc-tools",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.19",
|
4
4
|
"description": "Hornbill HDocBook Development Support Tool",
|
5
5
|
"main": "hdoc.js",
|
6
6
|
"bin": {
|
@@ -50,8 +50,9 @@
|
|
50
50
|
"mime-types": "^2.1.35",
|
51
51
|
"multer": "^1.4.5-lts.1",
|
52
52
|
"prompt": "^1.3.0",
|
53
|
-
"puppeteer": "^19.
|
53
|
+
"puppeteer": "^19.8.0",
|
54
54
|
"stream": "0.0.2",
|
55
|
+
"true-case-path": "^2.2.1",
|
55
56
|
"words-count": "^2.0.2",
|
56
57
|
"zip-a-folder": "^1.1.5"
|
57
58
|
}
|