hdoc-tools 0.8.11 → 0.8.12
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 +128 -39
- package/hdoc-init.js +1 -1
- package/hdoc-module.js +18 -2
- package/package.json +1 -1
package/hdoc-build.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
const {
|
|
5
5
|
createHash
|
|
6
|
-
} = require('crypto'),
|
|
6
|
+
} = require('crypto'), {
|
|
7
|
+
lutimesSync
|
|
8
|
+
} = require('fs'),
|
|
7
9
|
dree = require('dree'),
|
|
8
10
|
fs = require('fs-extra'),
|
|
9
11
|
mdfm = require('markdown-it-front-matter'),
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
includes_found = 0,
|
|
34
36
|
includes_success = 0,
|
|
35
37
|
includes_failed = 0,
|
|
38
|
+
book_read_time = 0,
|
|
36
39
|
hdocbook_project,
|
|
37
40
|
docId = '',
|
|
38
41
|
md_files = [],
|
|
@@ -49,16 +52,20 @@
|
|
|
49
52
|
|
|
50
53
|
let html_txt_updated = false;
|
|
51
54
|
|
|
55
|
+
let fm_headers = [];
|
|
56
|
+
let existing_fm_headers = false;
|
|
57
|
+
|
|
52
58
|
// Check if we have a frontmatter comment
|
|
53
59
|
const fm_header = hdoc.getHTMLFrontmatterHeader(html_txt);
|
|
54
60
|
if (Object.keys(fm_header.fm_properties).length > 0) {
|
|
61
|
+
existing_fm_headers = true;
|
|
62
|
+
|
|
55
63
|
// We have some frontmatter headers, check if title is one of them
|
|
56
64
|
let fm_title_found = false;
|
|
57
65
|
if (fm_header.fm_properties && fm_header.fm_properties.title !== undefined) {
|
|
58
66
|
// We have a title - but does the title have a value
|
|
59
67
|
if (fm_header.fm_properties.title === '') {
|
|
60
|
-
// No value - remove title from the properties map
|
|
61
|
-
// so we don't end up with 2 title properties, one empty and one with a value
|
|
68
|
+
// 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
|
|
62
69
|
delete fm_header.fm_properties.title;
|
|
63
70
|
} else {
|
|
64
71
|
// We have a value for the title property
|
|
@@ -66,6 +73,22 @@
|
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
// Is reading-time in the fm headers?
|
|
77
|
+
if (fm_header.fm_properties['reading-time'] === undefined) {
|
|
78
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
|
79
|
+
book_read_time += read_time_mins;
|
|
80
|
+
fm_header.fm_properties['reading-time'] = read_time_mins;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const key in fm_header.fm_properties) {
|
|
84
|
+
if (fm_header.fm_properties.hasOwnProperty(key)) {
|
|
85
|
+
fm_headers.push({
|
|
86
|
+
id: key,
|
|
87
|
+
value: fm_header.fm_properties[key]
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
69
92
|
if (!fm_title_found) {
|
|
70
93
|
// No frontmatter title found in properties
|
|
71
94
|
// Go get title from h tags in html
|
|
@@ -73,15 +96,10 @@
|
|
|
73
96
|
|
|
74
97
|
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
|
75
98
|
// We've found a heading tag, add that as a title to the existing frontmatter properties
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
frontmatter_header += '\n]]';
|
|
83
|
-
html_txt = html_txt.replace(fm_header.fm_header, frontmatter_header);
|
|
84
|
-
html_txt_updated = true;
|
|
99
|
+
fm_headers.push({
|
|
100
|
+
id: 'title',
|
|
101
|
+
value: html_heading[0].children[0].data
|
|
102
|
+
});
|
|
85
103
|
} else {
|
|
86
104
|
// No header tag, no frontmatter title, output a warning
|
|
87
105
|
console.log(`No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
|
@@ -90,16 +108,44 @@
|
|
|
90
108
|
} else {
|
|
91
109
|
// We have no frontmatter headers, get and build one from the html headings
|
|
92
110
|
const html_heading = hdoc.getFirstHTMLHeading(html_txt, h_tags_to_search);
|
|
111
|
+
|
|
112
|
+
// Add the title
|
|
93
113
|
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
|
94
114
|
// We've found a heading tag, add that as a title to the frontmatter content
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
115
|
+
fm_headers.push({
|
|
116
|
+
id: 'title',
|
|
117
|
+
value: html_heading[0].children[0].data
|
|
118
|
+
});
|
|
98
119
|
} else {
|
|
99
120
|
// No header tag, no frontmatter title, output a warning
|
|
100
121
|
console.log(`No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
|
101
122
|
}
|
|
123
|
+
|
|
124
|
+
// Add the reading time
|
|
125
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
|
126
|
+
book_read_time += read_time_mins;
|
|
127
|
+
fm_headers.push({
|
|
128
|
+
id: 'reading-time',
|
|
129
|
+
value: read_time_mins
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (fm_headers.length > 0) {
|
|
134
|
+
let fm_headers_content = '[[FRONTMATTER\n';
|
|
135
|
+
for (let i = 0; i < fm_headers.length; i++) {
|
|
136
|
+
fm_headers_content += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
|
137
|
+
}
|
|
138
|
+
fm_headers_content += ']]';
|
|
139
|
+
|
|
140
|
+
if (existing_fm_headers) {
|
|
141
|
+
html_txt = html_txt.replace(fm_header.fm_header, fm_headers_content);
|
|
142
|
+
html_txt_updated = true;
|
|
143
|
+
} else {
|
|
144
|
+
html_txt = `<!--${fm_headers_content}-->\n${html_txt}`;
|
|
145
|
+
html_txt_updated = true;
|
|
146
|
+
}
|
|
102
147
|
}
|
|
148
|
+
|
|
103
149
|
index_records.push({
|
|
104
150
|
relative_path: file_path.relativePath,
|
|
105
151
|
index_html: hdoc_index.transform_html_for_index(html_txt)
|
|
@@ -107,7 +153,7 @@
|
|
|
107
153
|
if (html_txt_updated) {
|
|
108
154
|
// Save HTML into HTML file
|
|
109
155
|
fs.writeFile(file_path.path, html_txt, function writeJSON(err) {
|
|
110
|
-
if (err) return console.log('Error writing:', target_file, '\
|
|
156
|
+
if (err) return console.log('Error writing:', target_file, '\n', err);
|
|
111
157
|
});
|
|
112
158
|
}
|
|
113
159
|
}
|
|
@@ -154,40 +200,75 @@
|
|
|
154
200
|
// Render markdown into HTML
|
|
155
201
|
let html_txt = md.render(md_txt.toString());
|
|
156
202
|
|
|
157
|
-
//
|
|
158
|
-
let
|
|
203
|
+
// Prepare frontmatter headers
|
|
204
|
+
let fm_headers = [];
|
|
159
205
|
let fm_content = frontmatter_content.split(/\r?\n/);
|
|
206
|
+
|
|
207
|
+
let fm_contains_title = false,
|
|
208
|
+
fm_contains_reading_time = false;
|
|
209
|
+
|
|
160
210
|
if (fm_content.length >= 0) {
|
|
161
211
|
fm_content.forEach(function (fm_prop) {
|
|
162
212
|
const fm_property = fm_prop.split(':');
|
|
163
|
-
if (fm_property[0] && fm_property[0]
|
|
213
|
+
if (fm_property[0] && fm_property[0].trim().length > 0 && fm_property[1] && fm_property[1].trim().length > 0) {
|
|
214
|
+
fm_headers.push({
|
|
215
|
+
id: fm_property[0].trim(),
|
|
216
|
+
value: fm_property[1].trim()
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (fm_property[0].trim() === 'title') {
|
|
220
|
+
fm_contains_title = true;
|
|
221
|
+
}
|
|
222
|
+
if (fm_property[0].trim() === 'reading-time') {
|
|
223
|
+
book_read_time += parseInt(fm_property[1].trim(), 10);
|
|
224
|
+
fm_contains_reading_time = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
164
227
|
});
|
|
165
228
|
}
|
|
166
229
|
|
|
230
|
+
// Does frontmatter tag contain a title property
|
|
167
231
|
if (!fm_contains_title) {
|
|
168
232
|
// Frontmatter tags don't contain a title property - go pull the first one from the html heading tags
|
|
169
233
|
const html_heading = hdoc.getFirstHTMLHeading(html_txt, h_tags_to_search);
|
|
170
234
|
|
|
171
235
|
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
|
172
236
|
// We've found a heading tag, add that as a title to the frontmatter content
|
|
173
|
-
|
|
174
|
-
|
|
237
|
+
fm_headers.push({
|
|
238
|
+
id: 'title',
|
|
239
|
+
value: html_heading[0].children[0].data.trim()
|
|
240
|
+
});
|
|
175
241
|
} else {
|
|
176
242
|
// No header tag, no frontmatter title, output a warning
|
|
177
243
|
console.log(`No frontmatter title property, or h1, h2 or h3 header tags detected in ${file_path}`);
|
|
178
244
|
}
|
|
179
245
|
}
|
|
180
246
|
|
|
247
|
+
// Does frontmatter tag contain a reading-time property
|
|
248
|
+
if (!fm_contains_reading_time) {
|
|
249
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
|
250
|
+
book_read_time += read_time_mins;
|
|
251
|
+
fm_headers.push({
|
|
252
|
+
id: 'reading-time',
|
|
253
|
+
value: read_time_mins
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
181
257
|
// Add frontmatter tags as comment to front of HTML
|
|
182
|
-
if (
|
|
183
|
-
|
|
258
|
+
if (fm_headers.length > 0) {
|
|
259
|
+
let fm_header = '<!--[[FRONTMATTER\n';
|
|
260
|
+
for (let i = 0; i < fm_headers.length; i++) {
|
|
261
|
+
fm_header += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
|
262
|
+
}
|
|
263
|
+
fm_header += ']]-->';
|
|
264
|
+
html_txt = `${fm_header}\n${html_txt}`;
|
|
184
265
|
}
|
|
185
266
|
|
|
186
267
|
// Save HTML into HTML file
|
|
187
268
|
const target_file = file_path.path.replace(path.extname(file_path.path), '.html');
|
|
188
269
|
const relative_path = file_path.relativePath.replace(path.extname(file_path.path), '.html');
|
|
189
270
|
fs.writeFile(target_file, html_txt, function writeJSON(err) {
|
|
190
|
-
if (err) return console.log('Error writing:', target_file, '\
|
|
271
|
+
if (err) return console.log('Error writing:', target_file, '\n', err);
|
|
191
272
|
});
|
|
192
273
|
|
|
193
274
|
const index_details = hdoc_index.transform_html_for_index(html_txt);
|
|
@@ -217,12 +298,8 @@
|
|
|
217
298
|
md_files.push(element);
|
|
218
299
|
} else {
|
|
219
300
|
// File is html, see if there's a matching md file and if there is then ignore the html
|
|
220
|
-
|
|
221
|
-
if (fs.existsSync(
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
html_path = element.path.replace(path.extname(element.path), '.htm');
|
|
225
|
-
if (fs.existsSync(html_path)) {
|
|
301
|
+
const md_path = element.path.replace(path.extname(element.path), '.md');
|
|
302
|
+
if (fs.existsSync(md_path)) {
|
|
226
303
|
return;
|
|
227
304
|
}
|
|
228
305
|
static_html_files.push(element);
|
|
@@ -264,8 +341,8 @@
|
|
|
264
341
|
// within the book.
|
|
265
342
|
// * Package everything up into a ZIP file, ready for the build controller to package and publish
|
|
266
343
|
|
|
267
|
-
console.log('Hornbill HDocBook Build', '\
|
|
268
|
-
console.log(' Document Path:', source_path, '\
|
|
344
|
+
console.log('Hornbill HDocBook Build', '\n');
|
|
345
|
+
console.log(' Document Path:', source_path, '\n');
|
|
269
346
|
|
|
270
347
|
// Load the hdocbook-project.json file to get the docId
|
|
271
348
|
// use the docId to get the book config
|
|
@@ -276,11 +353,13 @@
|
|
|
276
353
|
|
|
277
354
|
const book_path = path.join(source_path, docId),
|
|
278
355
|
hdocbook_path = path.join(book_path, 'hdocbook.json'),
|
|
279
|
-
hdocbook_config = require(hdocbook_path)
|
|
356
|
+
hdocbook_config = require(hdocbook_path),
|
|
357
|
+
work_path = path.join(source_path, '_work'),
|
|
358
|
+
work_hdocbook_path = path.join(work_path, docId, 'hdocbook.json');
|
|
280
359
|
|
|
281
|
-
console.log(`Building: ${docId} v${hdocbook_config.version}...\
|
|
360
|
+
console.log(`Building: ${docId} v${hdocbook_config.version}...\n`);
|
|
282
361
|
|
|
283
|
-
|
|
362
|
+
|
|
284
363
|
work_path_content = path.join(work_path, docId);
|
|
285
364
|
// Make _work folder to copy everything into
|
|
286
365
|
if (fs.existsSync(work_path)) {
|
|
@@ -295,7 +374,7 @@
|
|
|
295
374
|
try {
|
|
296
375
|
fs.copySync(path.join(source_path, docId), work_path_content);
|
|
297
376
|
} catch (e) {
|
|
298
|
-
console.error('Error copying from source_path:\
|
|
377
|
+
console.error('Error copying from source_path:\n', e);
|
|
299
378
|
process.exit(1);
|
|
300
379
|
}
|
|
301
380
|
|
|
@@ -315,12 +394,12 @@
|
|
|
315
394
|
|
|
316
395
|
console.log(` MD files found: ${conversion_attempted}`);
|
|
317
396
|
console.log(`Successfully converted to HTML: ${conversion_success}`);
|
|
318
|
-
console.log(` Failed to convert: ${conversion_failed}\
|
|
397
|
+
console.log(` Failed to convert: ${conversion_failed}\n`);
|
|
319
398
|
console.log(` Includes Found: ${includes_found}`);
|
|
320
399
|
console.log(` Includes Success: ${includes_success}`);
|
|
321
|
-
console.log(` Includes Failed: ${includes_failed}\
|
|
400
|
+
console.log(` Includes Failed: ${includes_failed}\n`);
|
|
322
401
|
|
|
323
|
-
console.log(` Static HTML Files Found: ${static_html_files.length}\
|
|
402
|
+
console.log(` Static HTML Files Found: ${static_html_files.length}\n`);
|
|
324
403
|
|
|
325
404
|
// Validate content
|
|
326
405
|
const validation_success = validate.run(work_path, docId, verbose);
|
|
@@ -393,6 +472,16 @@
|
|
|
393
472
|
}
|
|
394
473
|
|
|
395
474
|
|
|
475
|
+
// Add book read timing to the hdocbook.json
|
|
476
|
+
hdocbook_config.readingTime = Math.ceil(book_read_time + ((book_read_time / 100) * 10));
|
|
477
|
+
try {
|
|
478
|
+
fs.writeFileSync(work_hdocbook_path, JSON.stringify(hdocbook_config, null, 2));
|
|
479
|
+
console.log('\nhdocbook.json update success:', work_hdocbook_path);
|
|
480
|
+
} catch (e) {
|
|
481
|
+
console.log('\nError creating', work_hdocbook_path, ':', e);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
|
|
396
485
|
try {
|
|
397
486
|
const zip_path = path.join(work_path, docId + '.zip');
|
|
398
487
|
zipper.sync.zip(work_path_content).compress().save(zip_path);
|
package/hdoc-init.js
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
hdocbookFile.version = docProps.version;
|
|
90
90
|
hdocbookFile.navigation.items[0].items = [{
|
|
91
91
|
"text": "Welcome",
|
|
92
|
-
"link":
|
|
92
|
+
"link": docProps.id + '/index'
|
|
93
93
|
}];
|
|
94
94
|
fs.writeFile(hdocBookFilePath, JSON.stringify(hdocbookFile, null, 2), function writeJSON(err) {
|
|
95
95
|
if (err) return console.log('Error updating:', hdocBookFilePath, '\r\n', err);
|
package/hdoc-module.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const cheerio = require('cheerio'),
|
|
5
|
-
request = require('sync-request')
|
|
5
|
+
request = require('sync-request'),
|
|
6
|
+
html2text = require('html-to-text'),
|
|
7
|
+
wordsCount = require('words-count').default;
|
|
6
8
|
|
|
7
9
|
let includesCache = {};
|
|
8
10
|
|
|
@@ -196,5 +198,19 @@
|
|
|
196
198
|
if (str.length <= n) { return str; }
|
|
197
199
|
const subString = str.slice(0, n-1);
|
|
198
200
|
return (useWordBoundary ? subString.slice(0, subString.lastIndexOf(" ")) : subString) + '…';
|
|
199
|
-
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
exports.get_html_read_time = function(html) {
|
|
204
|
+
// Get word count
|
|
205
|
+
const text = html2text.convert(html, {
|
|
206
|
+
wordwrap: null
|
|
207
|
+
});
|
|
208
|
+
const word_count = wordsCount(text);
|
|
209
|
+
if (word_count === 0) return 0;
|
|
210
|
+
|
|
211
|
+
// Calculate the read time - divide the word count by 200
|
|
212
|
+
let read_time = Math.round(word_count / 200);
|
|
213
|
+
if (read_time === 0) read_time = 1;
|
|
214
|
+
return read_time;
|
|
215
|
+
};
|
|
200
216
|
})();
|