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 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
- let frontmatter_header = `[[FRONTMATTER\ntitle: ${html_heading[0].children[0].data}`;
77
- for (const key in fm_header.fm_properties) {
78
- if (fm_header.fm_properties.hasOwnProperty(key)) {
79
- frontmatter_header += `\n${key}: ${fm_header.fm_properties[key]}`;
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
- const frontmatter_header = `<!--[[FRONTMATTER\r\ntitle: ${html_heading[0].children[0].data}\r\n]]-->\r\n`;
96
- html_txt = frontmatter_header + html_txt;
97
- html_txt_updated = true;
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, '\r\n', err);
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
- // Does frontmatter tag contain a title property
158
- let fm_contains_title = false;
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] === 'title' && fm_property[1] && fm_property[1].length > 0) fm_contains_title = true;
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
- if (frontmatter_content.length > 0) frontmatter_content += '\r\n';
174
- frontmatter_content += `title: ${html_heading[0].children[0].data}`;
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 (frontmatter_content.length) {
183
- html_txt = "<!--[[FRONTMATTER\r\n" + frontmatter_content + "\r\n]]-->\r\n" + html_txt;
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, '\r\n', err);
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
- let html_path = element.path.replace(path.extname(element.path), '.html');
221
- if (fs.existsSync(html_path)) {
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', '\r\n');
268
- console.log(' Document Path:', source_path, '\r\n');
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}...\r\n`);
360
+ console.log(`Building: ${docId} v${hdocbook_config.version}...\n`);
282
361
 
283
- const work_path = path.join(source_path, '_work');
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:\r\n', e);
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}\r\n`);
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}\r\n`);
400
+ console.log(` Includes Failed: ${includes_failed}\n`);
322
401
 
323
- console.log(` Static HTML Files Found: ${static_html_files.length}\r\n`);
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": path.join(docProps.id, 'index')
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
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hdoc-tools",
3
- "version": "0.8.11",
3
+ "version": "0.8.12",
4
4
  "description": "Hornbill HDocBook Development Support Tool",
5
5
  "main": "hdoc.js",
6
6
  "bin": {