hdoc-tools 0.8.11 → 0.8.13

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/hdoc-stats.js CHANGED
@@ -60,7 +60,7 @@
60
60
  symbolicLinks: false
61
61
  };
62
62
 
63
- exports.run = function(ui_path, source_path, md, verbose = false) {
63
+ exports.run = function(ui_path, source_path, verbose = false) {
64
64
 
65
65
  // GERRY: The stats here are needed to support content development. The idea is to count all of the ]
66
66
  // words in a HDocBook so we know the size of the book, this helps with 3rd party involvement where
@@ -76,7 +76,7 @@
76
76
  // * MD files, and word count those
77
77
 
78
78
 
79
- console.log('Hornbill HDocBook Stats', '\r\n');
79
+ console.log('Hornbill HDocBook Stats : verbose=' + verbose, '\r\n');
80
80
 
81
81
  const project_json_path = path.join(source_path, 'hdocbook-project.json');
82
82
 
package/hdoc.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  (function () {
4
4
  'use strict';
@@ -55,7 +55,6 @@
55
55
  if (process.argv[x] === '-v') {
56
56
  verbose = true;
57
57
  }
58
-
59
58
  }
60
59
 
61
60
  console.log('Hornbill HDocBook Tools v' + getHdocPackageVersion(packageFile), '\r\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hdoc-tools",
3
- "version": "0.8.11",
3
+ "version": "0.8.13",
4
4
  "description": "Hornbill HDocBook Development Support Tool",
5
5
  "main": "hdoc.js",
6
6
  "bin": {
@@ -111,7 +111,10 @@ samp {
111
111
  }
112
112
 
113
113
  img,
114
- svg,
114
+ svg {
115
+ display: inline;
116
+ }
117
+
115
118
  video,
116
119
  canvas,
117
120
  audio,
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 Hornbill Docs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.