hdoc-tools 0.17.16 → 0.17.18
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 +2 -0
- package/hdoc-build-db.js +4 -1
- package/hdoc-build.js +56 -2
- package/hdoc-help.js +2 -1
- package/hdoc-validate.js +78 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -37,6 +37,8 @@ Returns statistics regarding the book you are working on:
|
|
37
37
|
|
38
38
|
If the -v switch is provided, then more verbose output is output, which includes a list of each MD and HTML file found, the file sizes, and file-specific word count.
|
39
39
|
|
40
|
+
The book statistics do not include counts for any externally hosted content injected into the book content using the [[INCLUDE]] tags.
|
41
|
+
|
40
42
|
### build
|
41
43
|
|
42
44
|
Performs a local build of the book, validates the links and static content are present and correct and outputs as a ZIP file.
|
package/hdoc-build-db.js
CHANGED
@@ -115,7 +115,10 @@
|
|
115
115
|
indexPromises.push(index_records[i]);
|
116
116
|
}
|
117
117
|
await Promise.all(indexPromises.map(async (file) => {
|
118
|
-
let index_path_name = file.relative_path.
|
118
|
+
let index_path_name = file.relative_path.replaceAll('\\', '/');
|
119
|
+
if (index_path_name.endsWith('/index.md') || index_path_name.endsWith('/index.html') || index_path_name.endsWith('/index.htm')) {
|
120
|
+
index_path_name = index_path_name.substring(0, index_path_name.lastIndexOf('/'));
|
121
|
+
}
|
119
122
|
index_path_name = '/' + index_path_name.replace(path.extname(file.relative_path), '');
|
120
123
|
let index_response = {
|
121
124
|
success: true,
|
package/hdoc-build.js
CHANGED
@@ -90,6 +90,8 @@
|
|
90
90
|
let fm_headers = [];
|
91
91
|
let existing_fm_headers = false;
|
92
92
|
let doc_type = 'Article';
|
93
|
+
let doc_title = '',
|
94
|
+
doc_description = '';
|
93
95
|
|
94
96
|
// Check if we have a frontmatter comment
|
95
97
|
const fm_header = hdoc.getHTMLFrontmatterHeader(html_txt);
|
@@ -106,6 +108,7 @@
|
|
106
108
|
} else {
|
107
109
|
// We have a value for the title property
|
108
110
|
fm_title_found = true;
|
111
|
+
doc_title = fm_header.fm_properties.title.trim();
|
109
112
|
}
|
110
113
|
}
|
111
114
|
|
@@ -139,15 +142,42 @@
|
|
139
142
|
id: 'title',
|
140
143
|
value: html_heading[0].children[0].data
|
141
144
|
});
|
145
|
+
doc_title = html_heading[0].children[0].data;
|
142
146
|
} else {
|
143
147
|
// No header tag, no frontmatter title, output a warning
|
144
148
|
console.log(`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
145
149
|
}
|
146
150
|
}
|
151
|
+
|
152
|
+
// Do we have a description header?
|
153
|
+
if (fm_header.fm_properties && fm_header.fm_properties.description !== undefined) {
|
154
|
+
if (fm_header.fm_properties.description === '') {
|
155
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
156
|
+
if (html_p_tag && html_p_tag[0] && html_p_tag[0].children && html_p_tag[0].children[0] && html_p_tag[0].children[0].data) {
|
157
|
+
fm_headers.push({
|
158
|
+
id: 'description',
|
159
|
+
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
160
|
+
});
|
161
|
+
}
|
162
|
+
} else {
|
163
|
+
fm_headers.push({
|
164
|
+
id: 'description',
|
165
|
+
value: fm_header.fm_properties.description.trim()
|
166
|
+
});
|
167
|
+
}
|
168
|
+
} else {
|
169
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
170
|
+
if (html_p_tag && html_p_tag[0] && html_p_tag[0].children && html_p_tag[0].children[0] && html_p_tag[0].children[0].data) {
|
171
|
+
fm_headers.push({
|
172
|
+
id: 'description',
|
173
|
+
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
174
|
+
});
|
175
|
+
}
|
176
|
+
}
|
147
177
|
} else {
|
148
178
|
// We have no frontmatter headers, get and build one from the html headings
|
149
179
|
const html_heading = hdoc.getFirstHTMLHeading(html_txt, h_tags_to_search);
|
150
|
-
|
180
|
+
let doc_title = '';
|
151
181
|
// Add the title
|
152
182
|
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
153
183
|
// We've found a heading tag, add that as a title to the frontmatter content
|
@@ -155,6 +185,7 @@
|
|
155
185
|
id: 'title',
|
156
186
|
value: html_heading[0].children[0].data
|
157
187
|
});
|
188
|
+
doc_title = html_heading[0].children[0].data;
|
158
189
|
} else if (file_path.name !== 'description_ext.md' && file_path.name !== 'article_ext.md') {
|
159
190
|
// No header tag, no frontmatter title, output a warning
|
160
191
|
console.log(`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
@@ -167,6 +198,14 @@
|
|
167
198
|
id: 'reading-time',
|
168
199
|
value: read_time_mins
|
169
200
|
});
|
201
|
+
|
202
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
203
|
+
if (html_p_tag && html_p_tag[0] && html_p_tag[0].children && html_p_tag[0].children[0] && html_p_tag[0].children[0].data) {
|
204
|
+
fm_headers.push({
|
205
|
+
id: 'description',
|
206
|
+
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
207
|
+
});
|
208
|
+
}
|
170
209
|
}
|
171
210
|
|
172
211
|
// Add doc type
|
@@ -357,6 +396,7 @@
|
|
357
396
|
|
358
397
|
let fm_contains_title = false,
|
359
398
|
fm_contains_reading_time = false,
|
399
|
+
fm_contains_description = false,
|
360
400
|
doc_title = '',
|
361
401
|
doc_type = 'Article';
|
362
402
|
|
@@ -382,6 +422,9 @@
|
|
382
422
|
book_read_time += parseInt(fm_val.trim(), 10);
|
383
423
|
fm_contains_reading_time = true;
|
384
424
|
}
|
425
|
+
if (fm_id.trim() === 'description') {
|
426
|
+
fm_contains_description = true;
|
427
|
+
}
|
385
428
|
}
|
386
429
|
});
|
387
430
|
}
|
@@ -389,7 +432,7 @@
|
|
389
432
|
// Add doc type
|
390
433
|
fm_headers.push({
|
391
434
|
id: 'type',
|
392
|
-
value: doc_type
|
435
|
+
value: doc_type,
|
393
436
|
});
|
394
437
|
|
395
438
|
// Does frontmatter tag contain a title property
|
@@ -410,6 +453,17 @@
|
|
410
453
|
}
|
411
454
|
}
|
412
455
|
|
456
|
+
// Does frontmatter contain a description header, generate one if not
|
457
|
+
if (!fm_contains_description) {
|
458
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
459
|
+
if (html_p_tag && html_p_tag[0] && html_p_tag[0].children && html_p_tag[0].children[0] && html_p_tag[0].children[0].data) {
|
460
|
+
fm_headers.push({
|
461
|
+
id: 'description',
|
462
|
+
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`
|
463
|
+
});
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
413
467
|
// Does frontmatter tag contain a reading-time property
|
414
468
|
if (!fm_contains_reading_time) {
|
415
469
|
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
package/hdoc-help.js
CHANGED
@@ -27,7 +27,8 @@ Commands
|
|
27
27
|
Starts a local web server on port 3000, serving the content. Supports a -port N to use a different port
|
28
28
|
|
29
29
|
- stats
|
30
|
-
Returns statistics regarding the book you are working on. Supports a -v switch for verbose output
|
30
|
+
Returns statistics regarding the book you are working on. Supports a -v switch for verbose output.
|
31
|
+
The book statistics do not include counts for any externally hosted content injected into the book content using the [[INCLUDE]] tags.
|
31
32
|
|
32
33
|
- validate
|
33
34
|
Validates the book content
|
package/hdoc-validate.js
CHANGED
@@ -86,6 +86,77 @@
|
|
86
86
|
return words;
|
87
87
|
};
|
88
88
|
|
89
|
+
const checkInline = async function (source_path, inline, excludes) {
|
90
|
+
let inline_errors = [];
|
91
|
+
for (let i = 0; i < inline.length; i++) {
|
92
|
+
const title = inline[i].title,
|
93
|
+
link = inline[i].link;
|
94
|
+
|
95
|
+
// Validate link segment spellings
|
96
|
+
const paths = link.split('/');
|
97
|
+
for (let i = 0; i < paths.length; i++) {
|
98
|
+
const path_words = paths[i].split('-');
|
99
|
+
for (let j = 0; j < path_words.length; j++) {
|
100
|
+
const translate_output = translator.translate(path_words[j], spellcheck_options);
|
101
|
+
if (Object.keys(translate_output).length) {
|
102
|
+
for (const spell_val in translate_output) {
|
103
|
+
if (translate_output.hasOwnProperty(spell_val)) {
|
104
|
+
for (const spelling in translate_output[spell_val][0]) {
|
105
|
+
if (translate_output[spell_val][0].hasOwnProperty(spelling)) {
|
106
|
+
if (!excludes[link]) {
|
107
|
+
inline_errors.push(`Inline Link [${link}] contains a British English spelling: ${spelling} should be ${translate_output[spell_val][0][spelling].details}`);
|
108
|
+
} else if (!excludes[link].includes(spelling.toLowerCase())) {
|
109
|
+
inline_errors.push(`Inline Link [${link}] contains a British English spelling: ${spelling} should be ${translate_output[spell_val][0][spelling].details}`);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
// Validate display names
|
120
|
+
const translate_output = translator.translate(title, spellcheck_options);
|
121
|
+
if (Object.keys(translate_output).length) {
|
122
|
+
for (const spell_val in translate_output) {
|
123
|
+
if (translate_output.hasOwnProperty(spell_val)) {
|
124
|
+
for (let j = 0; j < translate_output[spell_val].length; j++) {
|
125
|
+
for (const spelling in translate_output[spell_val][j]) {
|
126
|
+
if (translate_output[spell_val][j].hasOwnProperty(spelling)) {
|
127
|
+
if (!excludes[link]) {
|
128
|
+
inline_errors.push(`Inline title for link [${link}] contains a British English spelling: ${spelling} should be ${translate_output[spell_val][j][spelling].details}`);
|
129
|
+
} else if (!excludes[link].includes(spelling.toLowerCase())) {
|
130
|
+
inline_errors.push(`Inline title for link [${link}] contains a British English spelling: ${spelling} should be ${translate_output[spell_val][j][spelling].details}`);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
// Validate path exists - link should be a html file at this point as its after the content has been built
|
140
|
+
let file_exists = true;
|
141
|
+
let file_name = path.join(source_path, link + '.html');
|
142
|
+
if (!fs.existsSync(file_name)) {
|
143
|
+
file_name = path.join(source_path, link + '.htm');
|
144
|
+
if (!fs.existsSync(file_name)) {
|
145
|
+
file_name = path.join(source_path, link, 'index.html');
|
146
|
+
if (!fs.existsSync(file_name)) {
|
147
|
+
file_name = path.join(source_path, link, 'index.htm');
|
148
|
+
if (!fs.existsSync(file_name)) {
|
149
|
+
file_exists = false;
|
150
|
+
inline_errors.push(`Inline link [${link}] file does not exist.`);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return inline_errors;
|
158
|
+
};
|
159
|
+
|
89
160
|
const checkNavigation = async function (source_path, flat_nav, excludes) {
|
90
161
|
let nav_errors = [];
|
91
162
|
for (let key in flat_nav) {
|
@@ -506,10 +577,16 @@
|
|
506
577
|
}
|
507
578
|
}
|
508
579
|
|
509
|
-
// Check navigation spellings
|
580
|
+
// Check navigation spellings & paths exist
|
510
581
|
const nav_errors = await checkNavigation(source_path, nav_items, exclude_spellcheck);
|
511
582
|
if (nav_errors.length > 0) meta_errors.push(...nav_errors);
|
512
583
|
|
584
|
+
// Check inline content spellings & paths exist
|
585
|
+
if (hdocbook_config.inline && hdocbook_config.inline.length > 0) {
|
586
|
+
const inline_errors = await checkInline(source_path, hdocbook_config.inline, exclude_spellcheck);
|
587
|
+
if (inline_errors.length > 0) meta_errors.push(...inline_errors);
|
588
|
+
}
|
589
|
+
|
513
590
|
// Check redirects
|
514
591
|
const redirect_errors = await checkRedirects(source_path);
|
515
592
|
if (redirect_errors.length > 0) meta_errors.push(...redirect_errors);
|