hdoc-tools 0.17.26 → 0.17.27
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 +1416 -1026
- package/hdoc-validate.js +4 -4
- package/package.json +1 -1
package/hdoc-build.js
CHANGED
@@ -1,1090 +1,1480 @@
|
|
1
|
+
const { extension } = require("mime-types");
|
2
|
+
|
1
3
|
(function () {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
const crypto = require("crypto"),
|
7
|
+
dree = require("dree"),
|
8
|
+
fs = require("fs-extra"),
|
9
|
+
mdfm = require("markdown-it-front-matter"),
|
10
|
+
path = require("path"),
|
11
|
+
puppeteer = require("puppeteer"),
|
12
|
+
URL = require("url").URL,
|
13
|
+
hdoc_validate = require(path.join(__dirname, "hdoc-validate.js")),
|
14
|
+
hdoc = require(path.join(__dirname, "hdoc-module.js")),
|
15
|
+
hdoc_build_db = require(path.join(__dirname, "hdoc-build-db.js")),
|
16
|
+
hdoc_build_pdf = require(path.join(__dirname, "hdoc-build-pdf.js")),
|
17
|
+
hdoc_index = require(path.join(__dirname, "hdoc-db.js")),
|
18
|
+
archiver = require("archiver"),
|
19
|
+
xmlFormat = require("xml-formatter");
|
20
|
+
|
21
|
+
const h_tags_to_search = ["h1", "h2", "h3"],
|
22
|
+
doc_header_template_path = path.join(
|
23
|
+
__dirname,
|
24
|
+
"templates",
|
25
|
+
"doc-header.html"
|
26
|
+
),
|
27
|
+
non_git_doc_header_template_path = path.join(
|
28
|
+
__dirname,
|
29
|
+
"templates",
|
30
|
+
"doc-header-non-git.html"
|
31
|
+
),
|
32
|
+
pdf_header_template_path = path.join(
|
33
|
+
__dirname,
|
34
|
+
"templates",
|
35
|
+
"pdf-header.html"
|
36
|
+
),
|
37
|
+
non_git_pdf_header_template_path = path.join(
|
38
|
+
__dirname,
|
39
|
+
"templates",
|
40
|
+
"pdf-header-non-git.html"
|
41
|
+
),
|
42
|
+
pdf_template_path = path.join(__dirname, "templates", "pdf"),
|
43
|
+
pdf_template_file_path = path.join(pdf_template_path, "template.html"),
|
44
|
+
regex_version = /^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,6}$/,
|
45
|
+
h1_pattern = /(<h1.*?>)\s*.*\s*(.*<\/h1>)/,
|
46
|
+
regex_filename = /^[a-z]+-{0,1}([-a-z0-9]+)*$/;
|
47
|
+
|
48
|
+
let bc = {}, // Breadcrumbs map
|
49
|
+
built_file_hashes = [],
|
50
|
+
book_read_time = 0,
|
51
|
+
browser = {},
|
52
|
+
conversion_attempted = 0,
|
53
|
+
conversion_success = 0,
|
54
|
+
conversion_failed = 0,
|
55
|
+
css_templates = [],
|
56
|
+
doc_header_template = "",
|
57
|
+
doc_header_template_non_git = "",
|
58
|
+
errors_filename = [],
|
59
|
+
global_source_path = "",
|
60
|
+
pdf_created = 0,
|
61
|
+
pdf_enable = false,
|
62
|
+
pdf_header_template = "",
|
63
|
+
pdf_header_template_non_git = "",
|
64
|
+
pdf_template = "",
|
65
|
+
prod_families = {},
|
66
|
+
prods_supported = [],
|
67
|
+
doc_id = "",
|
68
|
+
git_token =
|
69
|
+
'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', // Github fine-grained personal access token that has minimum read-only access to Hornbill Docs metadata
|
70
|
+
hdocbook_config = {},
|
71
|
+
hdocbook_project,
|
72
|
+
includes_found = 0,
|
73
|
+
includes_success = 0,
|
74
|
+
includes_failed = 0,
|
75
|
+
index_records = [],
|
76
|
+
md_files = [],
|
77
|
+
md_files_delete = [],
|
78
|
+
redirects = {},
|
79
|
+
static_html_files = [],
|
80
|
+
work_path_content = "",
|
81
|
+
verbose = false;
|
82
|
+
|
83
|
+
const pdf_path_excluded = function (relative_path) {
|
84
|
+
if (
|
85
|
+
!hdocbook_project.pdfGeneration ||
|
86
|
+
hdocbook_project.pdfGeneration.exclude_paths === undefined
|
87
|
+
) {
|
88
|
+
return false;
|
89
|
+
}
|
90
|
+
if (relative_path.startsWith("/")) {
|
91
|
+
relative_path = relative_path.slice(1, relative_path.length);
|
92
|
+
}
|
93
|
+
for (
|
94
|
+
let i = 0;
|
95
|
+
i < hdocbook_project.pdfGeneration.exclude_paths.length;
|
96
|
+
i++
|
97
|
+
) {
|
98
|
+
const exclude_path = hdocbook_project.pdfGeneration.exclude_paths[i];
|
99
|
+
if (relative_path === exclude_path) return true;
|
100
|
+
if (exclude_path.at(-1) === "*") {
|
101
|
+
if (relative_path.startsWith(exclude_path.slice(0, -1))) {
|
102
|
+
return true;
|
69
103
|
}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
104
|
+
}
|
105
|
+
}
|
106
|
+
return false;
|
107
|
+
};
|
108
|
+
|
109
|
+
const transform_static_html = async function (file_path) {
|
110
|
+
if (fs.existsSync(file_path.path)) {
|
111
|
+
// Load HTML file
|
112
|
+
let html_txt = fs.readFileSync(file_path.path, "utf8");
|
113
|
+
html_txt = html_txt.replace(/\r/gm, ""); // Remove CR's so we're just dealing with newlines
|
114
|
+
|
115
|
+
let fm_headers = [];
|
116
|
+
let existing_fm_headers = false;
|
117
|
+
let doc_type = "Article";
|
118
|
+
let doc_title = "";
|
119
|
+
|
120
|
+
// Check if we have a frontmatter comment
|
121
|
+
const fm_header = hdoc.getHTMLFrontmatterHeader(html_txt);
|
122
|
+
if (Object.keys(fm_header.fm_properties).length > 0) {
|
123
|
+
existing_fm_headers = true;
|
124
|
+
|
125
|
+
// We have some frontmatter headers, check if title is one of them
|
126
|
+
let fm_title_found = false;
|
127
|
+
if (
|
128
|
+
fm_header.fm_properties &&
|
129
|
+
fm_header.fm_properties.title !== undefined
|
130
|
+
) {
|
131
|
+
// We have a title - but does the title have a value
|
132
|
+
if (fm_header.fm_properties.title === "") {
|
133
|
+
// 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
|
134
|
+
delete fm_header.fm_properties.title;
|
135
|
+
} else {
|
136
|
+
// We have a value for the title property
|
137
|
+
fm_title_found = true;
|
138
|
+
doc_title = fm_header.fm_properties.title.trim();
|
139
|
+
}
|
78
140
|
}
|
79
|
-
return false;
|
80
|
-
};
|
81
|
-
|
82
|
-
const transform_static_html = async function (file_path) {
|
83
|
-
if (fs.existsSync(file_path.path)) {
|
84
|
-
// Load HTML file
|
85
|
-
let html_txt = fs.readFileSync(file_path.path, 'utf8');
|
86
|
-
html_txt = html_txt.replace(/\r/gm, ''); // Remove CR's so we're just dealing with newlines
|
87
|
-
|
88
|
-
let fm_headers = [];
|
89
|
-
let existing_fm_headers = false;
|
90
|
-
let doc_type = 'Article';
|
91
|
-
let doc_title = '',
|
92
|
-
doc_description = '';
|
93
|
-
|
94
|
-
// Check if we have a frontmatter comment
|
95
|
-
const fm_header = hdoc.getHTMLFrontmatterHeader(html_txt);
|
96
|
-
if (Object.keys(fm_header.fm_properties).length > 0) {
|
97
|
-
existing_fm_headers = true;
|
98
|
-
|
99
|
-
// We have some frontmatter headers, check if title is one of them
|
100
|
-
let fm_title_found = false;
|
101
|
-
if (fm_header.fm_properties && fm_header.fm_properties.title !== undefined) {
|
102
|
-
// We have a title - but does the title have a value
|
103
|
-
if (fm_header.fm_properties.title === '') {
|
104
|
-
// 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
|
105
|
-
delete fm_header.fm_properties.title;
|
106
|
-
} else {
|
107
|
-
// We have a value for the title property
|
108
|
-
fm_title_found = true;
|
109
|
-
doc_title = fm_header.fm_properties.title.trim();
|
110
|
-
}
|
111
|
-
}
|
112
|
-
|
113
|
-
// Is reading-time in the fm headers?
|
114
|
-
if (fm_header.fm_properties['reading-time'] === undefined) {
|
115
|
-
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
116
|
-
book_read_time += read_time_mins;
|
117
|
-
fm_header.fm_properties['reading-time'] = read_time_mins;
|
118
|
-
}
|
119
|
-
|
120
|
-
for (const key in fm_header.fm_properties) {
|
121
|
-
if (fm_header.fm_properties.hasOwnProperty(key)) {
|
122
|
-
if (key === 'type') doc_type = fm_header.fm_properties[key];
|
123
|
-
else {
|
124
|
-
fm_headers.push({
|
125
|
-
id: key,
|
126
|
-
value: fm_header.fm_properties[key]
|
127
|
-
});
|
128
|
-
}
|
129
|
-
}
|
130
|
-
}
|
131
|
-
|
132
|
-
if (!fm_title_found && file_path.name !== 'description_ext.md' && file_path.name !== 'article_ext.md') {
|
133
|
-
// No frontmatter title found in properties
|
134
|
-
// Go get title from h tags in html
|
135
|
-
const html_heading = hdoc.getFirstHTMLHeading(html_txt, h_tags_to_search);
|
136
|
-
|
137
|
-
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
138
|
-
// We've found a heading tag, add that as a title to the existing frontmatter properties
|
139
|
-
fm_headers.push({
|
140
|
-
id: 'title',
|
141
|
-
value: html_heading[0].children[0].data
|
142
|
-
});
|
143
|
-
doc_title = html_heading[0].children[0].data;
|
144
|
-
} else {
|
145
|
-
// No header tag, no frontmatter title, output a warning
|
146
|
-
console.log(`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
147
|
-
}
|
148
|
-
}
|
149
|
-
|
150
|
-
// Do we have a description header?
|
151
|
-
if (fm_header.fm_properties && fm_header.fm_properties.description !== undefined) {
|
152
|
-
if (fm_header.fm_properties.description === '') {
|
153
|
-
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
154
|
-
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) {
|
155
|
-
fm_headers.push({
|
156
|
-
id: 'description',
|
157
|
-
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
158
|
-
});
|
159
|
-
}
|
160
|
-
} else {
|
161
|
-
fm_headers.push({
|
162
|
-
id: 'description',
|
163
|
-
value: fm_header.fm_properties.description.trim()
|
164
|
-
});
|
165
|
-
}
|
166
|
-
} else {
|
167
|
-
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
168
|
-
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) {
|
169
|
-
fm_headers.push({
|
170
|
-
id: 'description',
|
171
|
-
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
172
|
-
});
|
173
|
-
}
|
174
|
-
}
|
175
|
-
} else {
|
176
|
-
// We have no frontmatter headers, get and build one from the html headings
|
177
|
-
const html_heading = hdoc.getFirstHTMLHeading(html_txt, h_tags_to_search);
|
178
|
-
let doc_title = '';
|
179
|
-
// Add the title
|
180
|
-
if (html_heading && html_heading[0] && html_heading[0].children && html_heading[0].children[0] && html_heading[0].children[0].data) {
|
181
|
-
// We've found a heading tag, add that as a title to the frontmatter content
|
182
|
-
fm_headers.push({
|
183
|
-
id: 'title',
|
184
|
-
value: html_heading[0].children[0].data
|
185
|
-
});
|
186
|
-
doc_title = html_heading[0].children[0].data;
|
187
|
-
} else if (file_path.name !== 'description_ext.md' && file_path.name !== 'article_ext.md') {
|
188
|
-
// No header tag, no frontmatter title, output a warning
|
189
|
-
console.log(`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(', ')} tags detected in ${file_path.path}`);
|
190
|
-
}
|
191
|
-
|
192
|
-
// Add the reading time
|
193
|
-
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
194
|
-
book_read_time += read_time_mins;
|
195
|
-
fm_headers.push({
|
196
|
-
id: 'reading-time',
|
197
|
-
value: read_time_mins
|
198
|
-
});
|
199
|
-
|
200
|
-
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
201
|
-
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) {
|
202
|
-
fm_headers.push({
|
203
|
-
id: 'description',
|
204
|
-
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`.trim()
|
205
|
-
});
|
206
|
-
}
|
207
|
-
}
|
208
|
-
|
209
|
-
// Add doc type
|
210
|
-
fm_headers.push({
|
211
|
-
id: 'type',
|
212
|
-
value: doc_type
|
213
|
-
});
|
214
|
-
|
215
|
-
let metadata = {};
|
216
|
-
|
217
|
-
// Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
|
218
|
-
let html_h1 = h1_pattern.exec(html_txt);
|
219
|
-
if (html_h1 && html_h1[0])
|
220
|
-
html_h1 = html_h1[0].replace(/(<h1.*?>)/, '').replace(/(<\/h1>)/, '');
|
221
|
-
|
222
|
-
html_txt = html_txt.replace(h1_pattern, '');
|
223
|
-
|
224
|
-
// Get contributor data from Github, if exists
|
225
|
-
let contribs = [];
|
226
|
-
let last_commit = null;
|
227
|
-
if (hdocbook_config.publicSource && hdocbook_config.publicSource !== '' && hdocbook_config.publicSource.includes('github.com/Hornbill-Docs')) {
|
228
|
-
|
229
|
-
const github_paths = hdoc.get_github_api_path(hdocbook_config.publicSource, file_path.relativePath);
|
230
|
-
const contributors = hdoc.get_github_contributors(github_paths.api_path, git_token);
|
231
|
-
|
232
|
-
if (!contributors.success) {
|
233
|
-
console.log(`Error retrieving contributors from Github: ${contributors.error}`);
|
234
|
-
} else {
|
235
|
-
last_commit = contributors.last_commit_date;
|
236
|
-
metadata.last_commit = contributors.last_commit_date;
|
237
|
-
metadata.contributor_count = contributors.contributor_count;
|
238
|
-
metadata.edit_url = github_paths.edit_path;
|
239
|
-
contribs = contributors.contributors;
|
240
|
-
contributors.editPath = github_paths.edit_path;
|
241
|
-
fm_headers.push({
|
242
|
-
id: 'contributor-count',
|
243
|
-
value: contributors.contributor_count
|
244
|
-
});
|
245
|
-
fm_headers.push({
|
246
|
-
id: 'last-commit',
|
247
|
-
value: contributors.last_commit_date
|
248
|
-
});
|
249
|
-
const target_file = file_path.path.replace(path.extname(file_path.path), '._info.json');
|
250
|
-
delete contributors.success;
|
251
|
-
delete contributors.error;
|
252
|
-
contributors.editPath = github_paths.edit_path;
|
253
|
-
|
254
|
-
}
|
255
|
-
fm_headers.push({
|
256
|
-
id: 'edit-path',
|
257
|
-
value: github_paths.edit_path
|
258
|
-
});
|
259
|
-
}
|
260
|
-
|
261
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
262
|
-
fm_headers.push({
|
263
|
-
id: 'pdf-path',
|
264
|
-
value: file_path.relativePath.replace(path.extname(file_path.relativePath), '.pdf')
|
265
|
-
})
|
266
|
-
}
|
267
|
-
|
268
|
-
let fm_header_content = '<!--[[FRONTMATTER\n';
|
269
|
-
if (fm_headers.length > 0) {
|
270
|
-
for (let i = 0; i < fm_headers.length; i++) {
|
271
|
-
fm_header_content += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
272
|
-
}
|
273
|
-
fm_header_content += ']]-->';
|
274
|
-
|
275
|
-
if (existing_fm_headers) {
|
276
|
-
html_txt = html_txt.replace('<!--' + fm_header.fm_header + '-->', '');
|
277
|
-
}
|
278
|
-
}
|
279
|
-
|
280
|
-
let doc_header = '';
|
281
|
-
let pdf_header = '';
|
282
|
-
const inline_content = file_path.relativePath.startsWith(`${hdocbook_config.docId}/_inline/`);
|
283
|
-
if (hdocbook_config.publicSource && hdocbook_config.publicSource.includes('github.com/Hornbill-Docs')) {
|
284
|
-
// Build doc header from template and frontmatter tags
|
285
|
-
if (!inline_content)
|
286
|
-
doc_header = process_doc_header(fm_headers, file_path.relativePath, doc_header_template, html_h1);
|
287
|
-
|
288
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
289
|
-
pdf_header = process_doc_header(fm_headers, file_path.relativePath, pdf_header_template, html_h1);
|
290
|
-
} else {
|
291
|
-
if (!inline_content)
|
292
|
-
doc_header = process_doc_header(fm_headers, file_path.relativePath, doc_header_template_non_git, html_h1);
|
293
|
-
|
294
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
295
|
-
pdf_header = process_doc_header(fm_headers, file_path.relativePath, pdf_header_template_non_git, html_h1);
|
296
|
-
}
|
297
|
-
|
298
|
-
let pdf_size = 0;
|
299
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
300
|
-
let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
|
301
|
-
pdf_txt = `${pdf_header}\n${pdf_txt}`;
|
302
|
-
|
303
|
-
// Generate PDF file from HTML
|
304
|
-
const pdf_file_path = file_path.path.replace(path.extname(file_path.path), '.pdf');
|
305
|
-
pdf_size = await hdoc_build_pdf.generate_pdf(browser, pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, css_templates, verbose);
|
306
|
-
}
|
307
|
-
if (pdf_size > 0) pdf_created++;
|
308
|
-
|
309
|
-
if (inline_content)
|
310
|
-
html_txt = `${fm_header_content}\n${html_txt}`;
|
311
|
-
else
|
312
|
-
html_txt = `${fm_header_content}\n${doc_header}\n${html_txt}`;
|
313
|
-
|
314
|
-
let relative_path = file_path.relativePath;
|
315
|
-
if (!bc[relative_path.replace('.html', '')] && bc[relative_path.replace('/index.html', '')]) {
|
316
|
-
relative_path = relative_path.replace('/index.html', '');
|
317
|
-
}
|
318
141
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
md5: file_path.hash,
|
326
|
-
lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
|
327
|
-
inline: inline_content
|
328
|
-
});
|
142
|
+
// Is reading-time in the fm headers?
|
143
|
+
if (fm_header.fm_properties["reading-time"] === undefined) {
|
144
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
145
|
+
book_read_time += read_time_mins;
|
146
|
+
fm_header.fm_properties["reading-time"] = read_time_mins;
|
147
|
+
}
|
329
148
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
149
|
+
for (const key in fm_header.fm_properties) {
|
150
|
+
if (fm_header.fm_properties.hasOwnProperty(key)) {
|
151
|
+
if (key === "type") doc_type = fm_header.fm_properties[key];
|
152
|
+
else {
|
153
|
+
fm_headers.push({
|
154
|
+
id: key,
|
155
|
+
value: fm_header.fm_properties[key],
|
156
|
+
});
|
335
157
|
}
|
158
|
+
}
|
336
159
|
}
|
337
|
-
};
|
338
|
-
|
339
|
-
const transform_markdown_and_save_html = async function (file_path) {
|
340
|
-
conversion_attempted++;
|
341
|
-
|
342
|
-
if (fs.existsSync(file_path.path)) {
|
343
|
-
// Load markdown file
|
344
|
-
let md_txt = hdoc.expand_variables(fs.readFileSync(file_path.path, 'utf8'));
|
345
|
-
|
346
|
-
// Pull in external includes
|
347
|
-
const includes_processed = await hdoc.process_includes(file_path.path, md_txt, global_source_path);
|
348
|
-
md_txt = includes_processed.body.toString();
|
349
|
-
includes_found += includes_processed.found;
|
350
|
-
includes_success += includes_processed.success;
|
351
|
-
includes_failed += includes_processed.failed;
|
352
|
-
if (includes_processed.errors.length > 0) {
|
353
|
-
for (let i = 0; i < includes_processed.errors.length; i++) {
|
354
|
-
console.error(includes_processed.errors[i]);
|
355
|
-
}
|
356
|
-
}
|
357
160
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
//
|
378
|
-
|
379
|
-
|
380
|
-
|
161
|
+
if (
|
162
|
+
!fm_title_found &&
|
163
|
+
file_path.name !== "description_ext.md" &&
|
164
|
+
file_path.name !== "article_ext.md"
|
165
|
+
) {
|
166
|
+
// No frontmatter title found in properties
|
167
|
+
// Go get title from h tags in html
|
168
|
+
const html_heading = hdoc.getFirstHTMLHeading(
|
169
|
+
html_txt,
|
170
|
+
h_tags_to_search
|
171
|
+
);
|
172
|
+
|
173
|
+
if (
|
174
|
+
html_heading &&
|
175
|
+
html_heading[0] &&
|
176
|
+
html_heading[0].children &&
|
177
|
+
html_heading[0].children[0] &&
|
178
|
+
html_heading[0].children[0].data
|
179
|
+
) {
|
180
|
+
// We've found a heading tag, add that as a title to the existing frontmatter properties
|
181
|
+
fm_headers.push({
|
182
|
+
id: "title",
|
183
|
+
value: html_heading[0].children[0].data,
|
381
184
|
});
|
185
|
+
doc_title = html_heading[0].children[0].data;
|
186
|
+
} else {
|
187
|
+
// No header tag, no frontmatter title, output a warning
|
188
|
+
console.log(
|
189
|
+
`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(
|
190
|
+
", "
|
191
|
+
)} tags detected in ${file_path.path}`
|
192
|
+
);
|
193
|
+
}
|
194
|
+
}
|
382
195
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
const fm_id = fm_prop.slice(0, fm_prop.indexOf(':'));
|
404
|
-
const fm_val = fm_prop.slice(fm_prop.indexOf(':') + 1);
|
405
|
-
|
406
|
-
if (fm_id && fm_id.trim().length > 0 && fm_val && fm_val.trim().length > 0) {
|
407
|
-
fm_headers.push({
|
408
|
-
id: fm_id.trim(),
|
409
|
-
value: fm_val.trim()
|
410
|
-
});
|
411
|
-
|
412
|
-
if (fm_id.trim() === 'title') {
|
413
|
-
fm_contains_title = true;
|
414
|
-
doc_title = fm_val.trim();
|
415
|
-
}
|
416
|
-
if (fm_id.trim() === 'type') {
|
417
|
-
doc_type = fm_val.trim();
|
418
|
-
}
|
419
|
-
if (fm_id.trim() === 'reading-time') {
|
420
|
-
book_read_time += parseInt(fm_val.trim(), 10);
|
421
|
-
fm_contains_reading_time = true;
|
422
|
-
}
|
423
|
-
if (fm_id.trim() === 'description') {
|
424
|
-
fm_contains_description = true;
|
425
|
-
}
|
426
|
-
}
|
427
|
-
});
|
196
|
+
// Do we have a description header?
|
197
|
+
if (
|
198
|
+
fm_header.fm_properties &&
|
199
|
+
fm_header.fm_properties.description !== undefined
|
200
|
+
) {
|
201
|
+
if (fm_header.fm_properties.description === "") {
|
202
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
|
203
|
+
if (
|
204
|
+
html_p_tag &&
|
205
|
+
html_p_tag[0] &&
|
206
|
+
html_p_tag[0].children &&
|
207
|
+
html_p_tag[0].children[0] &&
|
208
|
+
html_p_tag[0].children[0].data
|
209
|
+
) {
|
210
|
+
fm_headers.push({
|
211
|
+
id: "description",
|
212
|
+
value: `${doc_title}: ${
|
213
|
+
html_p_tag[0].children[0].data.split(".")[0] + "."
|
214
|
+
}`.trim(),
|
215
|
+
});
|
428
216
|
}
|
429
|
-
|
430
|
-
// Add doc type
|
217
|
+
} else {
|
431
218
|
fm_headers.push({
|
432
|
-
|
433
|
-
|
219
|
+
id: "description",
|
220
|
+
value: fm_header.fm_properties.description.trim(),
|
434
221
|
});
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
console.log(`[WARNING] No frontmatter title property, or h1, h2 or h3 header tags detected in ${file_path.path}`);
|
451
|
-
}
|
452
|
-
}
|
453
|
-
|
454
|
-
// Does frontmatter contain a description header, generate one if not
|
455
|
-
if (!fm_contains_description) {
|
456
|
-
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ['p']);
|
457
|
-
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) {
|
458
|
-
fm_headers.push({
|
459
|
-
id: 'description',
|
460
|
-
value: `${doc_title}: ${html_p_tag[0].children[0].data.split('.')[0] + '.'}`
|
461
|
-
});
|
462
|
-
}
|
463
|
-
}
|
464
|
-
|
465
|
-
// Does frontmatter tag contain a reading-time property
|
466
|
-
if (!fm_contains_reading_time) {
|
467
|
-
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
468
|
-
book_read_time += read_time_mins;
|
469
|
-
fm_headers.push({
|
470
|
-
id: 'reading-time',
|
471
|
-
value: read_time_mins
|
472
|
-
});
|
473
|
-
}
|
474
|
-
let metadata = {};
|
475
|
-
|
476
|
-
// Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
|
477
|
-
let html_h1 = h1_pattern.exec(html_txt);
|
478
|
-
if (html_h1 && html_h1[0])
|
479
|
-
html_h1 = html_h1[0].replace(/(<h1.*?>)/, '').replace(/(<\/h1>)/, '');
|
480
|
-
|
481
|
-
html_txt = html_txt.replace(h1_pattern, '');
|
482
|
-
|
483
|
-
// Get contributor data from Github, if exists
|
484
|
-
let contribs = [];
|
485
|
-
let last_commit = null;
|
486
|
-
if (hdocbook_config.publicSource && hdocbook_config.publicSource !== '' && hdocbook_config.publicSource.includes('github.com/Hornbill-Docs')) {
|
487
|
-
|
488
|
-
const github_paths = hdoc.get_github_api_path(hdocbook_config.publicSource, file_path.relativePath);
|
489
|
-
const contributors = await hdoc.get_github_contributors(github_paths.api_path, git_token, hdocbook_config.publicSource);
|
490
|
-
|
491
|
-
if (!contributors.success) {
|
492
|
-
console.log(`Error retrieving contributors from Github: ${contributors.error}`);
|
493
|
-
} else {
|
494
|
-
last_commit = contributors.last_commit_date;
|
495
|
-
metadata.last_commit = contributors.last_commit_date;
|
496
|
-
metadata.contributor_count = contributors.contributor_count;
|
497
|
-
metadata.edit_url = github_paths.edit_path;
|
498
|
-
contribs = contributors.contributors;
|
499
|
-
contributors.editPath = github_paths.edit_path;
|
500
|
-
|
501
|
-
fm_headers.push({
|
502
|
-
id: 'contributor-count',
|
503
|
-
value: contributors.contributor_count
|
504
|
-
});
|
505
|
-
fm_headers.push({
|
506
|
-
id: 'last-commit',
|
507
|
-
value: contributors.last_commit_date
|
508
|
-
});
|
509
|
-
const target_file = file_path.path.replace(path.extname(file_path.path), '._info.json');
|
510
|
-
delete contributors.success;
|
511
|
-
delete contributors.error;
|
512
|
-
contributors.editPath = github_paths.edit_path;
|
513
|
-
try {
|
514
|
-
fs.writeFileSync(target_file, JSON.stringify(contributors, null, 2));
|
515
|
-
} catch (err) {
|
516
|
-
console.log('Error writing:', target_file, '\n', err);
|
517
|
-
}
|
518
|
-
}
|
519
|
-
fm_headers.push({
|
520
|
-
id: 'edit-path',
|
521
|
-
value: github_paths.edit_path
|
522
|
-
});
|
523
|
-
}
|
524
|
-
|
525
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
526
|
-
fm_headers.push({
|
527
|
-
id: 'pdf-path',
|
528
|
-
value: file_path.relativePath.replace(path.extname(file_path.relativePath), '.pdf')
|
529
|
-
})
|
530
|
-
}
|
531
|
-
|
532
|
-
// Add frontmatter tags as comment
|
533
|
-
let fm_header = '<!--[[FRONTMATTER\n';
|
534
|
-
if (fm_headers.length > 0) {
|
535
|
-
for (let i = 0; i < fm_headers.length; i++) {
|
536
|
-
fm_header += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
537
|
-
}
|
538
|
-
}
|
539
|
-
fm_header += ']]-->';
|
540
|
-
|
541
|
-
let doc_header = '';
|
542
|
-
let pdf_header = '';
|
543
|
-
const inline_content = file_path.relativePath.startsWith(`${hdocbook_config.docId}/_inline/`);
|
544
|
-
if (hdocbook_config.publicSource && hdocbook_config.publicSource.includes('github.com/Hornbill-Docs')) {
|
545
|
-
// Build doc header from template and frontmatter tags
|
546
|
-
if (!inline_content)
|
547
|
-
doc_header = process_doc_header(fm_headers, file_path.relativePath, doc_header_template, html_h1);
|
548
|
-
|
549
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
550
|
-
pdf_header = process_doc_header(fm_headers, file_path.relativePath, pdf_header_template, html_h1);
|
551
|
-
} else {
|
552
|
-
// Build doc header from template and frontmatter tags
|
553
|
-
if (!inline_content)
|
554
|
-
doc_header = process_doc_header(fm_headers, file_path.relativePath, doc_header_template_non_git, html_h1);
|
555
|
-
|
556
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
557
|
-
pdf_header = process_doc_header(fm_headers, file_path.relativePath, pdf_header_template_non_git, html_h1);
|
558
|
-
}
|
559
|
-
|
560
|
-
let pdf_size = 0;
|
561
|
-
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
562
|
-
let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
|
563
|
-
pdf_txt = `${pdf_header}\n${pdf_txt}`;
|
564
|
-
|
565
|
-
// Generate PDF file from HTML
|
566
|
-
const pdf_file_path = file_path.path.replace(path.extname(file_path.path), '.pdf');
|
567
|
-
pdf_size = await hdoc_build_pdf.generate_pdf(browser, pdf_template_path, pdf_template, hdocbook_config, pdf_txt, pdf_file_path, css_templates, verbose);
|
568
|
-
}
|
569
|
-
if (pdf_size > 0) pdf_created++;
|
570
|
-
|
571
|
-
if (inline_content)
|
572
|
-
html_txt = `${fm_header}\n${html_txt}`;
|
573
|
-
else
|
574
|
-
html_txt = `${fm_header}\n${doc_header}\n${html_txt}`;
|
575
|
-
|
576
|
-
// Save HTML into HTML file
|
577
|
-
const target_file = file_path.path.replace(path.extname(file_path.path), '.html');
|
578
|
-
let relative_path = file_path.relativePath.replace(path.extname(file_path.path), '.html');
|
579
|
-
try {
|
580
|
-
fs.writeFileSync(target_file, html_txt);
|
581
|
-
} catch (err) {
|
582
|
-
console.log('Error writing:', target_file, '\n', err);
|
583
|
-
}
|
584
|
-
|
585
|
-
if (!bc[relative_path.replace('.html', '')] && bc[relative_path.replace('/index.html', '')]) {
|
586
|
-
relative_path = relative_path.replace('/index.html', '');
|
587
|
-
}
|
588
|
-
|
589
|
-
index_records.push({
|
590
|
-
relative_path: relative_path,
|
591
|
-
index_html: hdoc_index.transform_html_for_index(html_txt),
|
592
|
-
metadata: metadata,
|
593
|
-
contributors: contribs,
|
594
|
-
pdf_size: pdf_size,
|
595
|
-
md5: file_path.hash,
|
596
|
-
lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
|
597
|
-
inline: inline_content
|
222
|
+
}
|
223
|
+
} else {
|
224
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
|
225
|
+
if (
|
226
|
+
html_p_tag &&
|
227
|
+
html_p_tag[0] &&
|
228
|
+
html_p_tag[0].children &&
|
229
|
+
html_p_tag[0].children[0] &&
|
230
|
+
html_p_tag[0].children[0].data
|
231
|
+
) {
|
232
|
+
fm_headers.push({
|
233
|
+
id: "description",
|
234
|
+
value: `${doc_title}: ${
|
235
|
+
html_p_tag[0].children[0].data.split(".")[0] + "."
|
236
|
+
}`.trim(),
|
598
237
|
});
|
599
|
-
|
600
|
-
// Add MD file to delete queue
|
601
|
-
md_files_delete.push(file_path.path)
|
602
|
-
|
603
|
-
conversion_success++;
|
604
|
-
return true;
|
238
|
+
}
|
605
239
|
}
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
240
|
+
} else {
|
241
|
+
// We have no frontmatter headers, get and build one from the html headings
|
242
|
+
const html_heading = hdoc.getFirstHTMLHeading(
|
243
|
+
html_txt,
|
244
|
+
h_tags_to_search
|
245
|
+
);
|
246
|
+
let doc_title = "";
|
247
|
+
// Add the title
|
248
|
+
if (
|
249
|
+
html_heading &&
|
250
|
+
html_heading[0] &&
|
251
|
+
html_heading[0].children &&
|
252
|
+
html_heading[0].children[0] &&
|
253
|
+
html_heading[0].children[0].data
|
254
|
+
) {
|
255
|
+
// We've found a heading tag, add that as a title to the frontmatter content
|
256
|
+
fm_headers.push({
|
257
|
+
id: "title",
|
258
|
+
value: html_heading[0].children[0].data,
|
259
|
+
});
|
260
|
+
doc_title = html_heading[0].children[0].data;
|
261
|
+
} else if (
|
262
|
+
file_path.name !== "description_ext.md" &&
|
263
|
+
file_path.name !== "article_ext.md"
|
264
|
+
) {
|
265
|
+
// No header tag, no frontmatter title, output a warning
|
266
|
+
console.log(
|
267
|
+
`[WARNING] No frontmatter title property, or ${h_tags_to_search.join(
|
268
|
+
", "
|
269
|
+
)} tags detected in ${file_path.path}`
|
270
|
+
);
|
625
271
|
}
|
626
272
|
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
273
|
+
// Add the reading time
|
274
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
275
|
+
book_read_time += read_time_mins;
|
276
|
+
fm_headers.push({
|
277
|
+
id: "reading-time",
|
278
|
+
value: read_time_mins,
|
279
|
+
});
|
280
|
+
|
281
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
|
282
|
+
if (
|
283
|
+
html_p_tag &&
|
284
|
+
html_p_tag[0] &&
|
285
|
+
html_p_tag[0].children &&
|
286
|
+
html_p_tag[0].children[0] &&
|
287
|
+
html_p_tag[0].children[0].data
|
288
|
+
) {
|
289
|
+
fm_headers.push({
|
290
|
+
id: "description",
|
291
|
+
value: `${doc_title}: ${
|
292
|
+
html_p_tag[0].children[0].data.split(".")[0] + "."
|
293
|
+
}`.trim(),
|
294
|
+
});
|
645
295
|
}
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
296
|
+
}
|
297
|
+
|
298
|
+
// Add doc type
|
299
|
+
fm_headers.push({
|
300
|
+
id: "type",
|
301
|
+
value: doc_type,
|
302
|
+
});
|
303
|
+
|
304
|
+
let metadata = {};
|
305
|
+
|
306
|
+
// Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
|
307
|
+
let html_h1 = h1_pattern.exec(html_txt);
|
308
|
+
if (html_h1 && html_h1[0])
|
309
|
+
html_h1 = html_h1[0].replace(/(<h1.*?>)/, "").replace(/(<\/h1>)/, "");
|
310
|
+
|
311
|
+
html_txt = html_txt.replace(h1_pattern, "");
|
312
|
+
|
313
|
+
// Get contributor data from Github, if exists
|
314
|
+
let contribs = [];
|
315
|
+
let last_commit = null;
|
316
|
+
if (
|
317
|
+
hdocbook_config.publicSource &&
|
318
|
+
hdocbook_config.publicSource !== "" &&
|
319
|
+
hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
|
320
|
+
) {
|
321
|
+
const github_paths = hdoc.get_github_api_path(
|
322
|
+
hdocbook_config.publicSource,
|
323
|
+
file_path.relativePath
|
324
|
+
);
|
325
|
+
const contributors = hdoc.get_github_contributors(
|
326
|
+
github_paths.api_path,
|
327
|
+
git_token
|
328
|
+
);
|
329
|
+
|
330
|
+
if (!contributors.success) {
|
331
|
+
console.log(
|
332
|
+
`Error retrieving contributors from Github: ${contributors.error}`
|
333
|
+
);
|
334
|
+
} else {
|
335
|
+
last_commit = contributors.last_commit_date;
|
336
|
+
metadata.last_commit = contributors.last_commit_date;
|
337
|
+
metadata.contributor_count = contributors.contributor_count;
|
338
|
+
metadata.edit_url = github_paths.edit_path;
|
339
|
+
contribs = contributors.contributors;
|
340
|
+
contributors.editPath = github_paths.edit_path;
|
341
|
+
fm_headers.push({
|
342
|
+
id: "contributor-count",
|
343
|
+
value: contributors.contributor_count,
|
344
|
+
});
|
345
|
+
fm_headers.push({
|
346
|
+
id: "last-commit",
|
347
|
+
value: contributors.last_commit_date,
|
348
|
+
});
|
349
|
+
const target_file = file_path.path.replace(
|
350
|
+
path.extname(file_path.path),
|
351
|
+
"._info.json"
|
352
|
+
);
|
353
|
+
delete contributors.success;
|
354
|
+
delete contributors.error;
|
355
|
+
contributors.editPath = github_paths.edit_path;
|
655
356
|
}
|
656
|
-
|
357
|
+
fm_headers.push({
|
358
|
+
id: "edit-path",
|
359
|
+
value: github_paths.edit_path,
|
360
|
+
});
|
361
|
+
}
|
362
|
+
|
363
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
364
|
+
fm_headers.push({
|
365
|
+
id: "pdf-path",
|
366
|
+
value: file_path.relativePath.replace(
|
367
|
+
path.extname(file_path.relativePath),
|
368
|
+
".pdf"
|
369
|
+
),
|
370
|
+
});
|
371
|
+
}
|
372
|
+
|
373
|
+
let fm_header_content = "<!--[[FRONTMATTER\n";
|
374
|
+
if (fm_headers.length > 0) {
|
657
375
|
for (let i = 0; i < fm_headers.length; i++) {
|
658
|
-
|
659
|
-
case 'title':
|
660
|
-
if (!used_h1)
|
661
|
-
wip_doc_header = wip_doc_header.replaceAll('{{title}}', fm_headers[i].value);
|
662
|
-
break;
|
663
|
-
case 'reading-time':
|
664
|
-
wip_doc_header = wip_doc_header.replaceAll('{{reading-time}}', fm_headers[i].value);
|
665
|
-
break;
|
666
|
-
case 'contributor-count':
|
667
|
-
wip_doc_header = wip_doc_header.replaceAll('{{contributor-count}}', fm_headers[i].value);
|
668
|
-
break;
|
669
|
-
case 'type':
|
670
|
-
wip_doc_header = wip_doc_header.replaceAll('{{doc-type}}', fm_headers[i].value);
|
671
|
-
break;
|
672
|
-
case 'edit-path':
|
673
|
-
wip_doc_header = wip_doc_header.replaceAll('{{edit-url}}', fm_headers[i].value);
|
674
|
-
break;
|
675
|
-
case 'last-commit':
|
676
|
-
let last_commit_date = fm_headers[i].value;
|
677
|
-
if (last_commit_date !== 'No Commit Date Available') {
|
678
|
-
last_commit_date = new Date(fm_headers[i].value).toDateString();
|
679
|
-
}
|
680
|
-
wip_doc_header = wip_doc_header.replaceAll('{{last-update}}', last_commit_date);
|
681
|
-
break;
|
682
|
-
}
|
376
|
+
fm_header_content += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
683
377
|
}
|
378
|
+
fm_header_content += "]]-->";
|
684
379
|
|
685
|
-
|
686
|
-
|
687
|
-
const bc_for_path = bc[logical_path];
|
688
|
-
let bc_tags = '\n';
|
689
|
-
if (bc_for_path) {
|
690
|
-
for (let i = 0; i < bc_for_path.length - 1; i++) {
|
691
|
-
let bc_link = '/';
|
692
|
-
if (redirects[bc_for_path[i].link]) {
|
693
|
-
if (redirects[bc_for_path[i].link].location) {
|
694
|
-
bc_link += redirects[bc_for_path[i].link].location;
|
695
|
-
}
|
696
|
-
} else {
|
697
|
-
if (bc_for_path[i].link) {
|
698
|
-
bc_link = bc_for_path[i].link.startsWith('/') ? bc_for_path[i].link : `/${bc_for_path[i].link}`;
|
699
|
-
} else {
|
700
|
-
bc_link = '';
|
701
|
-
}
|
702
|
-
|
703
|
-
}
|
704
|
-
if (bc_link !== '') {
|
705
|
-
bc_tags += `\t\t\t\t<li class="mt-0 nav-bar-item"><a href="${bc_link}" class="ps-0 pe-0 text-decoration-none">${bc_for_path[i].text}</a></li>\n`;
|
706
|
-
} else {
|
707
|
-
bc_tags += `\t\t\t\t<li class="mt-0 nav-bar-item">${bc_for_path[i].text}</li>\n`;
|
708
|
-
}
|
709
|
-
}
|
710
|
-
} else {
|
711
|
-
if (verbose) {
|
712
|
-
console.log(`[WARNING] Path is not present in navigation items: ${logical_path}`);
|
713
|
-
}
|
380
|
+
if (existing_fm_headers) {
|
381
|
+
html_txt = html_txt.replace("<!--" + fm_header.fm_header + "-->", "");
|
714
382
|
}
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
383
|
+
}
|
384
|
+
|
385
|
+
let doc_header = "";
|
386
|
+
let pdf_header = "";
|
387
|
+
const inline_content = file_path.relativePath.startsWith(
|
388
|
+
`${hdocbook_config.docId}/_inline/`
|
389
|
+
);
|
390
|
+
if (
|
391
|
+
hdocbook_config.publicSource &&
|
392
|
+
hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
|
393
|
+
) {
|
394
|
+
// Build doc header from template and frontmatter tags
|
395
|
+
if (!inline_content)
|
396
|
+
doc_header = process_doc_header(
|
397
|
+
fm_headers,
|
398
|
+
file_path.relativePath,
|
399
|
+
doc_header_template,
|
400
|
+
html_h1
|
401
|
+
);
|
402
|
+
|
403
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
404
|
+
pdf_header = process_doc_header(
|
405
|
+
fm_headers,
|
406
|
+
file_path.relativePath,
|
407
|
+
pdf_header_template,
|
408
|
+
html_h1
|
409
|
+
);
|
410
|
+
} else {
|
411
|
+
if (!inline_content)
|
412
|
+
doc_header = process_doc_header(
|
413
|
+
fm_headers,
|
414
|
+
file_path.relativePath,
|
415
|
+
doc_header_template_non_git,
|
416
|
+
html_h1
|
417
|
+
);
|
418
|
+
|
419
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
420
|
+
pdf_header = process_doc_header(
|
421
|
+
fm_headers,
|
422
|
+
file_path.relativePath,
|
423
|
+
pdf_header_template_non_git,
|
424
|
+
html_h1
|
425
|
+
);
|
426
|
+
}
|
427
|
+
|
428
|
+
let pdf_size = 0;
|
429
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
430
|
+
let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
|
431
|
+
pdf_txt = `${pdf_header}\n${pdf_txt}`;
|
432
|
+
|
433
|
+
// Generate PDF file from HTML
|
434
|
+
const pdf_file_path = file_path.path.replace(
|
435
|
+
path.extname(file_path.path),
|
436
|
+
".pdf"
|
437
|
+
);
|
438
|
+
pdf_size = await hdoc_build_pdf.generate_pdf(
|
439
|
+
browser,
|
440
|
+
pdf_template_path,
|
441
|
+
pdf_template,
|
442
|
+
hdocbook_config,
|
443
|
+
pdf_txt,
|
444
|
+
pdf_file_path,
|
445
|
+
css_templates,
|
446
|
+
verbose
|
447
|
+
);
|
448
|
+
}
|
449
|
+
if (pdf_size > 0) pdf_created++;
|
450
|
+
|
451
|
+
if (inline_content) html_txt = `${fm_header_content}\n${html_txt}`;
|
452
|
+
else html_txt = `${fm_header_content}\n${doc_header}\n${html_txt}`;
|
453
|
+
|
454
|
+
let relative_path = file_path.relativePath;
|
455
|
+
if (
|
456
|
+
!bc[relative_path.replace(".html", "")] &&
|
457
|
+
bc[relative_path.replace("/index.html", "")]
|
458
|
+
) {
|
459
|
+
relative_path = relative_path.replace("/index.html", "");
|
460
|
+
}
|
461
|
+
|
462
|
+
index_records.push({
|
463
|
+
relative_path: relative_path,
|
464
|
+
index_html: hdoc_index.transform_html_for_index(html_txt),
|
465
|
+
metadata: metadata,
|
466
|
+
contributors: contribs,
|
467
|
+
pdf_size: pdf_size,
|
468
|
+
md5: file_path.hash,
|
469
|
+
lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
|
470
|
+
inline: inline_content,
|
471
|
+
});
|
472
|
+
|
473
|
+
// Save HTML into HTML file
|
474
|
+
try {
|
475
|
+
fs.writeFileSync(file_path.path, html_txt);
|
476
|
+
} catch (err) {
|
477
|
+
console.log("Error writing:", target_file, "\n", err);
|
478
|
+
}
|
479
|
+
}
|
480
|
+
};
|
481
|
+
|
482
|
+
const transform_markdown_and_save_html = async function (file_path) {
|
483
|
+
conversion_attempted++;
|
484
|
+
|
485
|
+
if (fs.existsSync(file_path.path)) {
|
486
|
+
// Load markdown file
|
487
|
+
let md_txt = hdoc.expand_variables(
|
488
|
+
fs.readFileSync(file_path.path, "utf8")
|
489
|
+
);
|
490
|
+
|
491
|
+
// Pull in external includes
|
492
|
+
const includes_processed = await hdoc.process_includes(
|
493
|
+
file_path.path,
|
494
|
+
md_txt,
|
495
|
+
global_source_path
|
496
|
+
);
|
497
|
+
md_txt = includes_processed.body.toString();
|
498
|
+
includes_found += includes_processed.found;
|
499
|
+
includes_success += includes_processed.success;
|
500
|
+
includes_failed += includes_processed.failed;
|
501
|
+
if (includes_processed.errors.length > 0) {
|
502
|
+
for (let i = 0; i < includes_processed.errors.length; i++) {
|
503
|
+
console.error(includes_processed.errors[i]);
|
737
504
|
}
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
505
|
+
}
|
506
|
+
|
507
|
+
// One markdown parser per file. Seems wrong, but doesn't work with a global one once past the first md file
|
508
|
+
// Steve - revisit this
|
509
|
+
const md = require("markdown-it")({
|
510
|
+
html: true,
|
511
|
+
linkify: true,
|
512
|
+
typographer: true,
|
513
|
+
});
|
514
|
+
md.linkify.set({
|
515
|
+
fuzzyEmail: false,
|
516
|
+
fuzzyLink: false,
|
517
|
+
fuzzyIP: false,
|
518
|
+
});
|
519
|
+
|
520
|
+
// Process Frontmatter tags
|
521
|
+
let frontmatter_content = "";
|
522
|
+
md.use(mdfm, function (fm) {
|
523
|
+
frontmatter_content = fm;
|
524
|
+
});
|
525
|
+
|
526
|
+
// Process tips
|
527
|
+
const tips = require(__dirname + "/custom_modules/tips.js");
|
528
|
+
md.use(tips, {
|
529
|
+
links: true,
|
530
|
+
});
|
531
|
+
|
532
|
+
// Tidy up ```json and ```xml code tags
|
533
|
+
|
534
|
+
if (md_txt.includes("```json") || md_txt.includes("```xml"))
|
535
|
+
md_txt = tidy_code_tags(md_txt, file_path.relativePath);
|
536
|
+
|
537
|
+
// Render markdown into HTML
|
538
|
+
let html_txt = md.render(md_txt);
|
539
|
+
|
540
|
+
// Prepare frontmatter headers
|
541
|
+
let fm_headers = [];
|
542
|
+
let fm_content = frontmatter_content.split(/\r?\n/);
|
543
|
+
|
544
|
+
let fm_contains_title = false,
|
545
|
+
fm_contains_reading_time = false,
|
546
|
+
fm_contains_description = false,
|
547
|
+
doc_title = "",
|
548
|
+
doc_type = "Article";
|
549
|
+
|
550
|
+
if (fm_content.length >= 0) {
|
551
|
+
fm_content.forEach(function (fm_prop) {
|
552
|
+
const fm_id = fm_prop.slice(0, fm_prop.indexOf(":"));
|
553
|
+
const fm_val = fm_prop.slice(fm_prop.indexOf(":") + 1);
|
554
|
+
|
555
|
+
if (
|
556
|
+
fm_id &&
|
557
|
+
fm_id.trim().length > 0 &&
|
558
|
+
fm_val &&
|
559
|
+
fm_val.trim().length > 0
|
560
|
+
) {
|
561
|
+
fm_headers.push({
|
562
|
+
id: fm_id.trim(),
|
563
|
+
value: fm_val.trim(),
|
746
564
|
});
|
747
|
-
}
|
748
|
-
};
|
749
|
-
|
750
|
-
const dreeOptions = {
|
751
|
-
hash: true,
|
752
|
-
extensions: ['md', 'html', 'htm'],
|
753
|
-
normalize: true,
|
754
|
-
stat: true
|
755
|
-
};
|
756
|
-
|
757
|
-
const md5DreeOptions = {
|
758
|
-
hash: true,
|
759
|
-
normalize: true,
|
760
|
-
sorted: true
|
761
|
-
};
|
762
|
-
|
763
|
-
exports.run = async function (source_path, verbose_output, github_api_token, validate, gen_exclude, build_version = '') {
|
764
|
-
if (github_api_token !== '') {
|
765
|
-
git_token = github_api_token;
|
766
|
-
}
|
767
|
-
global_source_path = source_path;
|
768
|
-
verbose = verbose_output;
|
769
|
-
|
770
|
-
const start_time = Date.now();
|
771
|
-
// GERRY: The purpose of this function is to create a zip file containing the hdocbook content,
|
772
|
-
// * Create a _work folder
|
773
|
-
// * copy the hdocbook content to the work folder
|
774
|
-
// * Render all markdown into side-by-side HTML file
|
775
|
-
// * Replace SERVER_VARS embedded in documents with the right version information etc.
|
776
|
-
// * Build an index (sqlite FTS5) by extracting text from all HTML content in the work
|
777
|
-
// folder, conceptually we are making a little mini website crawler to index all of the content
|
778
|
-
// within the book.
|
779
|
-
// * Package everything up into a ZIP file, ready for the build controller to package and publish
|
780
|
-
|
781
|
-
console.log('Hornbill HDocBook Build', '\n');
|
782
|
-
console.log(' Document Path:', source_path, '\n');
|
783
|
-
const build_start_dt = new Date().toLocaleString();
|
784
|
-
|
785
|
-
// Load the hdocbook-project.json file to get the docId
|
786
|
-
// use the docId to get the book config
|
787
|
-
const hdocbook_project_config_path = path.join(source_path, 'hdocbook-project.json');
|
788
|
-
try {
|
789
|
-
hdocbook_project = require(hdocbook_project_config_path);
|
790
|
-
} catch (e) {
|
791
|
-
console.log('File not found: hdocbook-project.json\n');
|
792
|
-
console.log('hdoc build/validate needs to be run in the root of a HDoc Book.\n');
|
793
|
-
process.exit(1);
|
794
|
-
}
|
795
|
-
doc_id = hdocbook_project.docId;
|
796
|
-
|
797
|
-
if (!validate && hdocbook_project.pdfGeneration !== undefined && hdocbook_project.pdfGeneration.enable !== undefined) {
|
798
|
-
pdf_enable = hdocbook_project.pdfGeneration.enable;
|
799
|
-
}
|
800
565
|
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
redirects[redirect_key] = hdocbook_project.redirects[i];
|
566
|
+
if (fm_id.trim() === "title") {
|
567
|
+
fm_contains_title = true;
|
568
|
+
doc_title = fm_val.trim();
|
805
569
|
}
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
work_path = path.join(source_path, '_work'),
|
813
|
-
work_hdocbook_path = path.join(work_path, doc_id, 'hdocbook.json');
|
814
|
-
|
815
|
-
hdocbook_config = require(hdocbook_path);
|
816
|
-
if (build_version !== '') {
|
817
|
-
if (build_version.match(regex_version)) {
|
818
|
-
hdocbook_config.version = build_version;
|
819
|
-
} else {
|
820
|
-
console.log(`\n[WARNING] Argument build version [${build_version}] does not match expected pattern, defaulting to version specified in book [${hdocbook_config.version}]\n`);
|
570
|
+
if (fm_id.trim() === "type") {
|
571
|
+
doc_type = fm_val.trim();
|
572
|
+
}
|
573
|
+
if (fm_id.trim() === "reading-time") {
|
574
|
+
book_read_time += parseInt(fm_val.trim(), 10);
|
575
|
+
fm_contains_reading_time = true;
|
821
576
|
}
|
577
|
+
if (fm_id.trim() === "description") {
|
578
|
+
fm_contains_description = true;
|
579
|
+
}
|
580
|
+
}
|
581
|
+
});
|
582
|
+
}
|
583
|
+
|
584
|
+
// Add doc type
|
585
|
+
fm_headers.push({
|
586
|
+
id: "type",
|
587
|
+
value: doc_type,
|
588
|
+
});
|
589
|
+
|
590
|
+
// Does frontmatter tag contain a title property
|
591
|
+
if (!fm_contains_title) {
|
592
|
+
// Frontmatter tags don't contain a title property - go pull the first one from the html heading tags
|
593
|
+
const html_heading = hdoc.getFirstHTMLHeading(
|
594
|
+
html_txt,
|
595
|
+
h_tags_to_search
|
596
|
+
);
|
597
|
+
|
598
|
+
if (
|
599
|
+
html_heading &&
|
600
|
+
html_heading[0] &&
|
601
|
+
html_heading[0].children &&
|
602
|
+
html_heading[0].children[0] &&
|
603
|
+
html_heading[0].children[0].data
|
604
|
+
) {
|
605
|
+
// We've found a heading tag, add that as a title to the frontmatter content
|
606
|
+
fm_headers.push({
|
607
|
+
id: "title",
|
608
|
+
value: html_heading[0].children[0].data.trim(),
|
609
|
+
});
|
610
|
+
doc_title = html_heading[0].children[0].data.trim();
|
611
|
+
} else if (
|
612
|
+
file_path.name !== "description_ext.md" &&
|
613
|
+
file_path.name !== "article_ext.md"
|
614
|
+
) {
|
615
|
+
// No header tag, no frontmatter title, output a warning
|
616
|
+
console.log(
|
617
|
+
`[WARNING] No frontmatter title property, or h1, h2 or h3 header tags detected in ${file_path.path}`
|
618
|
+
);
|
822
619
|
}
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
620
|
+
}
|
621
|
+
|
622
|
+
// Does frontmatter contain a description header, generate one if not
|
623
|
+
if (!fm_contains_description) {
|
624
|
+
const html_p_tag = hdoc.getFirstHTMLHeading(html_txt, ["p"]);
|
625
|
+
if (
|
626
|
+
html_p_tag &&
|
627
|
+
html_p_tag[0] &&
|
628
|
+
html_p_tag[0].children &&
|
629
|
+
html_p_tag[0].children[0] &&
|
630
|
+
html_p_tag[0].children[0].data
|
631
|
+
) {
|
632
|
+
fm_headers.push({
|
633
|
+
id: "description",
|
634
|
+
value: `${doc_title}: ${
|
635
|
+
html_p_tag[0].children[0].data.split(".")[0] + "."
|
636
|
+
}`,
|
637
|
+
});
|
827
638
|
}
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
const
|
833
|
-
|
834
|
-
|
835
|
-
|
639
|
+
}
|
640
|
+
|
641
|
+
// Does frontmatter tag contain a reading-time property
|
642
|
+
if (!fm_contains_reading_time) {
|
643
|
+
const read_time_mins = hdoc.get_html_read_time(html_txt);
|
644
|
+
book_read_time += read_time_mins;
|
645
|
+
fm_headers.push({
|
646
|
+
id: "reading-time",
|
647
|
+
value: read_time_mins,
|
648
|
+
});
|
649
|
+
}
|
650
|
+
let metadata = {};
|
651
|
+
|
652
|
+
// Remove the first <h1>title</h1> from the HTML as we'll add that in the document header
|
653
|
+
let html_h1 = h1_pattern.exec(html_txt);
|
654
|
+
if (html_h1 && html_h1[0])
|
655
|
+
html_h1 = html_h1[0].replace(/(<h1.*?>)/, "").replace(/(<\/h1>)/, "");
|
656
|
+
|
657
|
+
html_txt = html_txt.replace(h1_pattern, "");
|
658
|
+
|
659
|
+
// Get contributor data from Github, if exists
|
660
|
+
let contribs = [];
|
661
|
+
let last_commit = null;
|
662
|
+
if (
|
663
|
+
hdocbook_config.publicSource &&
|
664
|
+
hdocbook_config.publicSource !== "" &&
|
665
|
+
hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
|
666
|
+
) {
|
667
|
+
const github_paths = hdoc.get_github_api_path(
|
668
|
+
hdocbook_config.publicSource,
|
669
|
+
file_path.relativePath
|
670
|
+
);
|
671
|
+
const contributors = await hdoc.get_github_contributors(
|
672
|
+
github_paths.api_path,
|
673
|
+
git_token,
|
674
|
+
hdocbook_config.publicSource
|
675
|
+
);
|
676
|
+
|
677
|
+
if (!contributors.success) {
|
678
|
+
console.log(
|
679
|
+
`Error retrieving contributors from Github: ${contributors.error}`
|
680
|
+
);
|
836
681
|
} else {
|
837
|
-
|
838
|
-
|
682
|
+
last_commit = contributors.last_commit_date;
|
683
|
+
metadata.last_commit = contributors.last_commit_date;
|
684
|
+
metadata.contributor_count = contributors.contributor_count;
|
685
|
+
metadata.edit_url = github_paths.edit_path;
|
686
|
+
contribs = contributors.contributors;
|
687
|
+
contributors.editPath = github_paths.edit_path;
|
688
|
+
|
689
|
+
fm_headers.push({
|
690
|
+
id: "contributor-count",
|
691
|
+
value: contributors.contributor_count,
|
692
|
+
});
|
693
|
+
fm_headers.push({
|
694
|
+
id: "last-commit",
|
695
|
+
value: contributors.last_commit_date,
|
696
|
+
});
|
697
|
+
const target_file = file_path.path.replace(
|
698
|
+
path.extname(file_path.path),
|
699
|
+
"._info.json"
|
700
|
+
);
|
701
|
+
delete contributors.success;
|
702
|
+
delete contributors.error;
|
703
|
+
contributors.editPath = github_paths.edit_path;
|
704
|
+
try {
|
705
|
+
fs.writeFileSync(
|
706
|
+
target_file,
|
707
|
+
JSON.stringify(contributors, null, 2)
|
708
|
+
);
|
709
|
+
} catch (err) {
|
710
|
+
console.log("Error writing:", target_file, "\n", err);
|
711
|
+
}
|
839
712
|
}
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
713
|
+
fm_headers.push({
|
714
|
+
id: "edit-path",
|
715
|
+
value: github_paths.edit_path,
|
716
|
+
});
|
717
|
+
}
|
718
|
+
|
719
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
720
|
+
fm_headers.push({
|
721
|
+
id: "pdf-path",
|
722
|
+
value: file_path.relativePath.replace(
|
723
|
+
path.extname(file_path.relativePath),
|
724
|
+
".pdf"
|
725
|
+
),
|
726
|
+
});
|
727
|
+
}
|
728
|
+
|
729
|
+
// Add frontmatter tags as comment
|
730
|
+
let fm_header = "<!--[[FRONTMATTER\n";
|
731
|
+
if (fm_headers.length > 0) {
|
732
|
+
for (let i = 0; i < fm_headers.length; i++) {
|
733
|
+
fm_header += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
|
855
734
|
}
|
735
|
+
}
|
736
|
+
fm_header += "]]-->";
|
737
|
+
|
738
|
+
let doc_header = "";
|
739
|
+
let pdf_header = "";
|
740
|
+
const inline_content = file_path.relativePath.startsWith(
|
741
|
+
`${hdocbook_config.docId}/_inline/`
|
742
|
+
);
|
743
|
+
if (
|
744
|
+
hdocbook_config.publicSource &&
|
745
|
+
hdocbook_config.publicSource.includes("github.com/Hornbill-Docs")
|
746
|
+
) {
|
747
|
+
// Build doc header from template and frontmatter tags
|
748
|
+
if (!inline_content)
|
749
|
+
doc_header = process_doc_header(
|
750
|
+
fm_headers,
|
751
|
+
file_path.relativePath,
|
752
|
+
doc_header_template,
|
753
|
+
html_h1
|
754
|
+
);
|
755
|
+
|
756
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
757
|
+
pdf_header = process_doc_header(
|
758
|
+
fm_headers,
|
759
|
+
file_path.relativePath,
|
760
|
+
pdf_header_template,
|
761
|
+
html_h1
|
762
|
+
);
|
763
|
+
} else {
|
764
|
+
// Build doc header from template and frontmatter tags
|
765
|
+
if (!inline_content)
|
766
|
+
doc_header = process_doc_header(
|
767
|
+
fm_headers,
|
768
|
+
file_path.relativePath,
|
769
|
+
doc_header_template_non_git,
|
770
|
+
html_h1
|
771
|
+
);
|
772
|
+
|
773
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath))
|
774
|
+
pdf_header = process_doc_header(
|
775
|
+
fm_headers,
|
776
|
+
file_path.relativePath,
|
777
|
+
pdf_header_template_non_git,
|
778
|
+
html_h1
|
779
|
+
);
|
780
|
+
}
|
781
|
+
|
782
|
+
let pdf_size = 0;
|
783
|
+
if (pdf_enable && !pdf_path_excluded(file_path.relativePath)) {
|
784
|
+
let pdf_txt = await hdoc_build_pdf.process_images(file_path, html_txt);
|
785
|
+
pdf_txt = `${pdf_header}\n${pdf_txt}`;
|
786
|
+
|
787
|
+
// Generate PDF file from HTML
|
788
|
+
const pdf_file_path = file_path.path.replace(
|
789
|
+
path.extname(file_path.path),
|
790
|
+
".pdf"
|
791
|
+
);
|
792
|
+
pdf_size = await hdoc_build_pdf.generate_pdf(
|
793
|
+
browser,
|
794
|
+
pdf_template_path,
|
795
|
+
pdf_template,
|
796
|
+
hdocbook_config,
|
797
|
+
pdf_txt,
|
798
|
+
pdf_file_path,
|
799
|
+
css_templates,
|
800
|
+
verbose
|
801
|
+
);
|
802
|
+
}
|
803
|
+
if (pdf_size > 0) pdf_created++;
|
804
|
+
|
805
|
+
if (inline_content) html_txt = `${fm_header}\n${html_txt}`;
|
806
|
+
else html_txt = `${fm_header}\n${doc_header}\n${html_txt}`;
|
807
|
+
|
808
|
+
// Save HTML into HTML file
|
809
|
+
const target_file = file_path.path.replace(
|
810
|
+
path.extname(file_path.path),
|
811
|
+
".html"
|
812
|
+
);
|
813
|
+
let relative_path = file_path.relativePath.replace(
|
814
|
+
path.extname(file_path.path),
|
815
|
+
".html"
|
816
|
+
);
|
817
|
+
try {
|
818
|
+
fs.writeFileSync(target_file, html_txt);
|
819
|
+
} catch (err) {
|
820
|
+
console.log("Error writing:", target_file, "\n", err);
|
821
|
+
}
|
822
|
+
|
823
|
+
if (
|
824
|
+
!bc[relative_path.replace(".html", "")] &&
|
825
|
+
bc[relative_path.replace("/index.html", "")]
|
826
|
+
) {
|
827
|
+
relative_path = relative_path.replace("/index.html", "");
|
828
|
+
}
|
829
|
+
|
830
|
+
index_records.push({
|
831
|
+
relative_path: relative_path,
|
832
|
+
index_html: hdoc_index.transform_html_for_index(html_txt),
|
833
|
+
metadata: metadata,
|
834
|
+
contributors: contribs,
|
835
|
+
pdf_size: pdf_size,
|
836
|
+
md5: file_path.hash,
|
837
|
+
lastmod: last_commit !== null ? last_commit : file_path.hb_lastmod,
|
838
|
+
inline: inline_content,
|
839
|
+
});
|
840
|
+
|
841
|
+
// Add MD file to delete queue
|
842
|
+
md_files_delete.push(file_path.path);
|
843
|
+
|
844
|
+
conversion_success++;
|
845
|
+
return true;
|
846
|
+
}
|
847
|
+
conversion_failed++;
|
848
|
+
console.error("MD file does not exist:", file_path.path);
|
849
|
+
return false;
|
850
|
+
};
|
851
|
+
|
852
|
+
const tidy_code_tags = function (markdown, file) {
|
853
|
+
const json_to_tidy = markdown.match(/```json[\r\n](\s|.)*?```/g);
|
854
|
+
if (json_to_tidy && json_to_tidy.length > 0) {
|
855
|
+
for (let i = 0; i < json_to_tidy.length; i++) {
|
856
|
+
if (json_to_tidy[i] !== "") {
|
857
|
+
let json_tidy = json_to_tidy[i]
|
858
|
+
.replace("```json", "")
|
859
|
+
.replace("```", "");
|
860
|
+
try {
|
861
|
+
json_tidy = JSON.stringify(JSON.parse(json_tidy), null, 2);
|
862
|
+
} catch (e) {
|
863
|
+
console.log(
|
864
|
+
`[WARNING] Could not tidy JSON in file [${file}]: ${e}`
|
865
|
+
);
|
866
|
+
}
|
867
|
+
markdown = markdown.replace(
|
868
|
+
json_to_tidy[i],
|
869
|
+
"```json\n" + json_tidy + "\n```"
|
870
|
+
);
|
871
|
+
}
|
872
|
+
}
|
873
|
+
}
|
856
874
|
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
875
|
+
const xml_to_tidy = markdown.match(/```xml[\r\n](\s|.)*?```/g);
|
876
|
+
if (xml_to_tidy && xml_to_tidy.length > 0) {
|
877
|
+
for (let i = 0; i < xml_to_tidy.length; i++) {
|
878
|
+
if (xml_to_tidy[i] !== "") {
|
879
|
+
const xml_tidy = xml_to_tidy[i]
|
880
|
+
.replace("```xml", "")
|
881
|
+
.replace("```", "");
|
882
|
+
let new_xml_string = xml_tidy;
|
883
|
+
try {
|
884
|
+
new_xml_string = xmlFormat(xml_tidy, {
|
885
|
+
indentation: " ",
|
886
|
+
collapseContent: true,
|
887
|
+
lineSeparator: "\n",
|
865
888
|
});
|
889
|
+
} catch (e) {
|
890
|
+
console.log(`[WARNING] Could not tidy XML in file [${file}]: ${e}`);
|
891
|
+
}
|
892
|
+
markdown = markdown.replace(
|
893
|
+
xml_to_tidy[i],
|
894
|
+
"```xml\n" + new_xml_string + "\n```"
|
895
|
+
);
|
866
896
|
}
|
867
|
-
|
897
|
+
}
|
898
|
+
}
|
899
|
+
return markdown;
|
900
|
+
};
|
901
|
+
|
902
|
+
const process_doc_header = function (fm_headers, doc_path, template, h1) {
|
903
|
+
let wip_doc_header = template;
|
904
|
+
let used_h1 = false;
|
905
|
+
if (h1 && h1 !== "") {
|
906
|
+
wip_doc_header = wip_doc_header.replaceAll("{{title}}", h1);
|
907
|
+
used_h1 = true;
|
908
|
+
}
|
909
|
+
// Process fm_headers properties first
|
910
|
+
for (let i = 0; i < fm_headers.length; i++) {
|
911
|
+
switch (fm_headers[i].id) {
|
912
|
+
case "title":
|
913
|
+
if (!used_h1)
|
914
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
915
|
+
"{{title}}",
|
916
|
+
fm_headers[i].value
|
917
|
+
);
|
918
|
+
break;
|
919
|
+
case "reading-time":
|
920
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
921
|
+
"{{reading-time}}",
|
922
|
+
fm_headers[i].value
|
923
|
+
);
|
924
|
+
break;
|
925
|
+
case "contributor-count":
|
926
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
927
|
+
"{{contributor-count}}",
|
928
|
+
fm_headers[i].value
|
929
|
+
);
|
930
|
+
break;
|
931
|
+
case "type":
|
932
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
933
|
+
"{{doc-type}}",
|
934
|
+
fm_headers[i].value
|
935
|
+
);
|
936
|
+
break;
|
937
|
+
case "edit-path":
|
938
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
939
|
+
"{{edit-url}}",
|
940
|
+
fm_headers[i].value
|
941
|
+
);
|
942
|
+
break;
|
943
|
+
case "last-commit":
|
944
|
+
let last_commit_date = fm_headers[i].value;
|
945
|
+
if (last_commit_date !== "No Commit Date Available") {
|
946
|
+
last_commit_date = new Date(fm_headers[i].value).toDateString();
|
947
|
+
}
|
948
|
+
wip_doc_header = wip_doc_header.replaceAll(
|
949
|
+
"{{last-update}}",
|
950
|
+
last_commit_date
|
951
|
+
);
|
952
|
+
break;
|
953
|
+
}
|
954
|
+
}
|
868
955
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
956
|
+
// Now sort out breadcrumbs
|
957
|
+
const logical_path = doc_path.replace(path.extname(doc_path), "");
|
958
|
+
const bc_for_path = bc[logical_path];
|
959
|
+
let bc_tags = "\n";
|
960
|
+
if (bc_for_path) {
|
961
|
+
for (let i = 0; i < bc_for_path.length - 1; i++) {
|
962
|
+
let bc_link = "/";
|
963
|
+
if (redirects[bc_for_path[i].link]) {
|
964
|
+
if (redirects[bc_for_path[i].link].location) {
|
965
|
+
bc_link += redirects[bc_for_path[i].link].location;
|
966
|
+
}
|
967
|
+
} else {
|
968
|
+
if (bc_for_path[i].link) {
|
969
|
+
bc_link = bc_for_path[i].link.startsWith("/")
|
970
|
+
? bc_for_path[i].link
|
971
|
+
: `/${bc_for_path[i].link}`;
|
972
|
+
} else {
|
973
|
+
bc_link = "";
|
974
|
+
}
|
975
|
+
}
|
976
|
+
if (bc_link !== "") {
|
977
|
+
bc_tags += `\t\t\t\t<li class="mt-0 nav-bar-item"><a href="${bc_link}" class="ps-0 pe-0 text-decoration-none">${bc_for_path[i].text}</a></li>\n`;
|
978
|
+
} else {
|
979
|
+
bc_tags += `\t\t\t\t<li class="mt-0 nav-bar-item">${bc_for_path[i].text}</li>\n`;
|
876
980
|
}
|
981
|
+
}
|
982
|
+
} else {
|
983
|
+
if (verbose) {
|
984
|
+
console.log(
|
985
|
+
`[WARNING] Path is not present in navigation items: ${logical_path}`
|
986
|
+
);
|
987
|
+
}
|
988
|
+
}
|
989
|
+
bc_tags += "\t\t\t";
|
990
|
+
wip_doc_header = wip_doc_header.replaceAll("{{breadcrumbs}}", bc_tags);
|
991
|
+
return wip_doc_header;
|
992
|
+
};
|
993
|
+
|
994
|
+
// File callback for build scan
|
995
|
+
const build_file_callback = function (element) {
|
996
|
+
if (element.extension === "md") {
|
997
|
+
element.hb_source_path = path.join(
|
998
|
+
global_source_path,
|
999
|
+
element.relativePath
|
1000
|
+
);
|
1001
|
+
const fstats = fs.statSync(element.hb_source_path);
|
1002
|
+
element.hb_lastmod = `${fstats.mtime.toISOString().slice(0, 19)}Z`;
|
1003
|
+
md_files.push(element);
|
1004
|
+
} else {
|
1005
|
+
// File is html, see if there's a matching md file and if there is then ignore the html
|
1006
|
+
const md_path = element.path.replace(path.extname(element.path), ".md");
|
1007
|
+
if (fs.existsSync(md_path)) {
|
1008
|
+
return;
|
1009
|
+
}
|
1010
|
+
element.hb_source_path = path.join(
|
1011
|
+
global_source_path,
|
1012
|
+
element.relativePath
|
1013
|
+
);
|
1014
|
+
const fstats = fs.statSync(element.hb_source_path);
|
1015
|
+
element.hb_lastmod = `${fstats.mtime.toISOString().slice(0, 19)}Z`;
|
1016
|
+
static_html_files.push(element);
|
1017
|
+
}
|
1018
|
+
};
|
1019
|
+
|
1020
|
+
// File & folder callback for MD5 hash of built content
|
1021
|
+
const hash_callback = function (element) {
|
1022
|
+
if (element.extension !== "db") {
|
1023
|
+
built_file_hashes.push({
|
1024
|
+
path: element.relativePath,
|
1025
|
+
hash: element.hash,
|
1026
|
+
});
|
1027
|
+
}
|
1028
|
+
};
|
1029
|
+
|
1030
|
+
// File scan callback for filename validation
|
1031
|
+
const filename_validation_callback = function (element) {
|
1032
|
+
if (element.relativePath.startsWith('_inline/')) return;
|
1033
|
+
if (element.name === 'article_ext.md' || element.name === 'description_ext.md') return;
|
1034
|
+
const file_no_ext = element.name.replace(`.${element.extension}`, '');
|
1035
|
+
if (!file_no_ext.match(regex_filename)) errors_filename.push(element.relativePath);
|
1036
|
+
};
|
1037
|
+
|
1038
|
+
const dreeOptions = {
|
1039
|
+
hash: true,
|
1040
|
+
extensions: ["md", "html", "htm"],
|
1041
|
+
normalize: true,
|
1042
|
+
stat: true,
|
1043
|
+
};
|
1044
|
+
|
1045
|
+
const dreeOptionsAllFiles = {
|
1046
|
+
descendants: true,
|
1047
|
+
excludeEmptyDirectories: true,
|
1048
|
+
hash: false,
|
1049
|
+
normalize: true,
|
1050
|
+
size: false,
|
1051
|
+
sizeInBytes: false,
|
1052
|
+
symbolicLinks: false,
|
1053
|
+
};
|
1054
|
+
|
1055
|
+
const md5DreeOptions = {
|
1056
|
+
hash: true,
|
1057
|
+
normalize: true,
|
1058
|
+
sorted: true,
|
1059
|
+
};
|
1060
|
+
|
1061
|
+
exports.run = async function (
|
1062
|
+
source_path,
|
1063
|
+
verbose_output,
|
1064
|
+
github_api_token,
|
1065
|
+
validate,
|
1066
|
+
gen_exclude,
|
1067
|
+
build_version = ""
|
1068
|
+
) {
|
1069
|
+
if (github_api_token !== "") {
|
1070
|
+
git_token = github_api_token;
|
1071
|
+
}
|
1072
|
+
global_source_path = source_path;
|
1073
|
+
verbose = verbose_output;
|
1074
|
+
|
1075
|
+
const start_time = Date.now();
|
1076
|
+
// GERRY: The purpose of this function is to create a zip file containing the hdocbook content,
|
1077
|
+
// * Create a _work folder
|
1078
|
+
// * copy the hdocbook content to the work folder
|
1079
|
+
// * Render all markdown into side-by-side HTML file
|
1080
|
+
// * Replace SERVER_VARS embedded in documents with the right version information etc.
|
1081
|
+
// * Build an index (sqlite FTS5) by extracting text from all HTML content in the work
|
1082
|
+
// folder, conceptually we are making a little mini website crawler to index all of the content
|
1083
|
+
// within the book.
|
1084
|
+
// * Package everything up into a ZIP file, ready for the build controller to package and publish
|
1085
|
+
|
1086
|
+
console.log("Hornbill HDocBook Build", "\n");
|
1087
|
+
console.log(" Document Path:", source_path, "\n");
|
1088
|
+
const build_start_dt = new Date().toLocaleString();
|
1089
|
+
|
1090
|
+
// Load the hdocbook-project.json file to get the docId
|
1091
|
+
// use the docId to get the book config
|
1092
|
+
const hdocbook_project_config_path = path.join(
|
1093
|
+
source_path,
|
1094
|
+
"hdocbook-project.json"
|
1095
|
+
);
|
1096
|
+
try {
|
1097
|
+
hdocbook_project = require(hdocbook_project_config_path);
|
1098
|
+
} catch (e) {
|
1099
|
+
console.log("File not found: hdocbook-project.json\n");
|
1100
|
+
console.log(
|
1101
|
+
"hdoc build/validate needs to be run in the root of a HDoc Book.\n"
|
1102
|
+
);
|
1103
|
+
process.exit(1);
|
1104
|
+
}
|
1105
|
+
doc_id = hdocbook_project.docId;
|
1106
|
+
|
1107
|
+
if (
|
1108
|
+
!validate &&
|
1109
|
+
hdocbook_project.pdfGeneration !== undefined &&
|
1110
|
+
hdocbook_project.pdfGeneration.enable !== undefined
|
1111
|
+
) {
|
1112
|
+
pdf_enable = hdocbook_project.pdfGeneration.enable;
|
1113
|
+
}
|
877
1114
|
|
878
|
-
|
879
|
-
|
1115
|
+
if (
|
1116
|
+
hdocbook_project.redirects &&
|
1117
|
+
hdocbook_project.redirects instanceof Array
|
1118
|
+
) {
|
1119
|
+
for (let i = 0; i < hdocbook_project.redirects.length; i++) {
|
1120
|
+
const redirect_key =
|
1121
|
+
hdocbook_project.redirects[i].url.indexOf("/") == 0
|
1122
|
+
? hdocbook_project.redirects[i].url.substring(1)
|
1123
|
+
: hdocbook_project.redirects[i].url;
|
1124
|
+
redirects[redirect_key] = hdocbook_project.redirects[i];
|
1125
|
+
}
|
1126
|
+
}
|
880
1127
|
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
1128
|
+
console.log(`Loading hdocbook config...`);
|
1129
|
+
|
1130
|
+
const book_path = path.join(source_path, doc_id),
|
1131
|
+
hdocbook_path = path.join(book_path, "hdocbook.json"),
|
1132
|
+
work_path = path.join(source_path, "_work"),
|
1133
|
+
work_hdocbook_path = path.join(work_path, doc_id, "hdocbook.json");
|
1134
|
+
|
1135
|
+
hdocbook_config = require(hdocbook_path);
|
1136
|
+
if (build_version !== "") {
|
1137
|
+
if (build_version.match(regex_version)) {
|
1138
|
+
hdocbook_config.version = build_version;
|
1139
|
+
} else {
|
1140
|
+
console.log(
|
1141
|
+
`\n[WARNING] Argument build version [${build_version}] does not match expected pattern, defaulting to version specified in book [${hdocbook_config.version}]\n`
|
1142
|
+
);
|
1143
|
+
}
|
1144
|
+
}
|
890
1145
|
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
} catch (e) {
|
898
|
-
console.log('\nError creating', checksum_path, ':', e);
|
899
|
-
process.exit(1);
|
900
|
-
}
|
1146
|
+
if (!hdocbook_config.version.match(regex_version)) {
|
1147
|
+
console.log(
|
1148
|
+
`ERROR: Version number does not match required format - ${hdocbook_config.version}\n`
|
1149
|
+
);
|
1150
|
+
process.exit(1);
|
1151
|
+
}
|
901
1152
|
|
902
|
-
|
903
|
-
|
1153
|
+
if (
|
1154
|
+
hdocbook_config.publicSource &&
|
1155
|
+
hdocbook_config.publicSource.endsWith(".git")
|
1156
|
+
)
|
1157
|
+
hdocbook_config.publicSource = hdocbook_config.publicSource.substring(
|
1158
|
+
0,
|
1159
|
+
hdocbook_config.publicSource.length - 4
|
1160
|
+
);
|
1161
|
+
|
1162
|
+
console.log(`Loading product families...`);
|
1163
|
+
const prods = await hdoc.load_product_families();
|
1164
|
+
if (!prods.success) {
|
1165
|
+
console.log(`${prods.errors}\n`);
|
1166
|
+
process.exit(1);
|
1167
|
+
} else {
|
1168
|
+
prod_families = prods.prod_families;
|
1169
|
+
prods_supported = prods.prods_supported;
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
if (!validate) {
|
1173
|
+
console.log("Caching CSS for PDF generation...");
|
1174
|
+
const css_files = [
|
1175
|
+
path.join(pdf_template_path, "css", "custom-block.css"),
|
1176
|
+
path.join(pdf_template_path, "css", "hdocs-pdf.css"),
|
1177
|
+
path.join(pdf_template_path, "css", "vars.css"),
|
1178
|
+
];
|
1179
|
+
for (let i = 0; i < css_files.length; i++) {
|
904
1180
|
try {
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
pdf_header_template_non_git = fs.readFileSync(non_git_pdf_header_template_path, 'utf8');
|
909
|
-
} catch (err) {
|
910
|
-
console.log(`Error reading document header template: ${err}`);
|
911
|
-
process.exit(1);
|
1181
|
+
css_templates.push(fs.readFileSync(css_files[i], "utf8"));
|
1182
|
+
} catch (e) {
|
1183
|
+
console.log(`Error reading file[${css_files[i]}]: ${e}`);
|
912
1184
|
}
|
1185
|
+
}
|
1186
|
+
}
|
913
1187
|
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
console.log('\r\n-----------------------');
|
927
|
-
console.log(' Validation Output ');
|
928
|
-
console.log('-----------------------');
|
929
|
-
console.log(`\n${bc_build.errors.length} errors found when processing navigation:\n`);
|
930
|
-
console.log(` - ${bc_build.errors.join('\n\n - ')}`);
|
931
|
-
console.log('\n');
|
932
|
-
process.exit(1);
|
933
|
-
}
|
934
|
-
bc = bc_build.bc;
|
935
|
-
console.log(`Processing content...`);
|
936
|
-
// Get a list of MD files in work_path
|
937
|
-
dree.scan(work_path, dreeOptions, build_file_callback);
|
938
|
-
|
939
|
-
if (pdf_enable) {
|
940
|
-
// Create a Chromium browser instance generate PDFs with
|
941
|
-
browser = await puppeteer.launch({ headless: 'new' });
|
942
|
-
}
|
1188
|
+
// Validate all filenames first
|
1189
|
+
console.log(`Validating book filenames meet kebab-case requirements...`);
|
1190
|
+
dree.scan(book_path, dreeOptionsAllFiles, filename_validation_callback);
|
1191
|
+
if (errors_filename.length > 0) {
|
1192
|
+
console.log("\r\n-----------------------");
|
1193
|
+
console.log(" Validation Output ");
|
1194
|
+
console.log("-----------------------");
|
1195
|
+
console.log(`${errors_filename.length} files do not meet filename requirements:`);
|
1196
|
+
console.log(` - ${errors_filename.join("\n - ")}`);
|
1197
|
+
console.log()
|
1198
|
+
process.exit(1);
|
1199
|
+
}
|
943
1200
|
|
944
|
-
|
945
|
-
let mdPromiseArray = [];
|
946
|
-
for (let i = 0; i < md_files.length; i++) {
|
947
|
-
mdPromiseArray.push(md_files[i]);
|
948
|
-
}
|
949
|
-
const chunkSize = 8;
|
950
|
-
for (let i = 0; i < mdPromiseArray.length; i += chunkSize) {
|
951
|
-
const chunk = mdPromiseArray.slice(i, i + chunkSize);
|
952
|
-
// do whatever
|
953
|
-
await Promise.all(chunk.map(async (file) => {
|
954
|
-
await transform_markdown_and_save_html(file);
|
955
|
-
}));
|
956
|
-
}
|
1201
|
+
console.log(`Building: ${doc_id} v${hdocbook_config.version}...\n`);
|
957
1202
|
|
1203
|
+
// Make _work folder to copy everything into
|
1204
|
+
work_path_content = path.join(work_path, doc_id);
|
1205
|
+
if (fs.existsSync(work_path)) {
|
1206
|
+
fs.rmSync(work_path, {
|
1207
|
+
recursive: true,
|
1208
|
+
force: true,
|
1209
|
+
});
|
1210
|
+
}
|
1211
|
+
fs.mkdirSync(work_path);
|
1212
|
+
|
1213
|
+
// Copy files from book into _work-doc_id folder
|
1214
|
+
console.log(`Copying content into work folder...`);
|
1215
|
+
try {
|
1216
|
+
fs.copySync(path.join(source_path, doc_id), work_path_content);
|
1217
|
+
} catch (e) {
|
1218
|
+
console.error("Error copying from source_path:\n", e);
|
1219
|
+
process.exit(1);
|
1220
|
+
}
|
958
1221
|
|
1222
|
+
// Create MD5 hash of content before build
|
1223
|
+
console.log(`Creating Hash...`);
|
959
1224
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
}
|
1225
|
+
dree.scan(work_path_content, md5DreeOptions, hash_callback);
|
1226
|
+
let concat_hash = "|";
|
1227
|
+
for (let i = 0; i < built_file_hashes.length; i++) {
|
1228
|
+
concat_hash +=
|
1229
|
+
built_file_hashes[i].path + ":" + built_file_hashes[i].hash + "|";
|
1230
|
+
}
|
1231
|
+
if (concat_hash === "|") {
|
1232
|
+
console.log("No hash of content has been returned.");
|
1233
|
+
process.exit(1);
|
1234
|
+
}
|
971
1235
|
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
1236
|
+
// Create hash and write file
|
1237
|
+
const hash = crypto.createHash("md5").update(concat_hash).digest("hex");
|
1238
|
+
const checksum_path = path.join(work_path_content, "checksum.md5");
|
1239
|
+
try {
|
1240
|
+
fs.writeFileSync(checksum_path, hash);
|
1241
|
+
console.log("Hash file creation success:", checksum_path);
|
1242
|
+
} catch (e) {
|
1243
|
+
console.log("\nError creating", checksum_path, ":", e);
|
1244
|
+
process.exit(1);
|
1245
|
+
}
|
976
1246
|
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
1247
|
+
// Load document header templates
|
1248
|
+
console.log(`Loading templates...`);
|
1249
|
+
try {
|
1250
|
+
doc_header_template = fs.readFileSync(doc_header_template_path, "utf8");
|
1251
|
+
doc_header_template_non_git = fs.readFileSync(
|
1252
|
+
non_git_doc_header_template_path,
|
1253
|
+
"utf8"
|
1254
|
+
);
|
1255
|
+
pdf_header_template = fs.readFileSync(pdf_header_template_path, "utf8");
|
1256
|
+
pdf_header_template_non_git = fs.readFileSync(
|
1257
|
+
non_git_pdf_header_template_path,
|
1258
|
+
"utf8"
|
1259
|
+
);
|
1260
|
+
} catch (err) {
|
1261
|
+
console.log(`Error reading document header template: ${err}`);
|
1262
|
+
process.exit(1);
|
1263
|
+
}
|
988
1264
|
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
1265
|
+
if (pdf_enable) {
|
1266
|
+
// Load PDF templates
|
1267
|
+
try {
|
1268
|
+
pdf_template = fs.readFileSync(pdf_template_file_path, "utf8");
|
1269
|
+
} catch (err) {
|
1270
|
+
console.log(`Error reading PDF template: ${err}`);
|
1271
|
+
process.exit(1);
|
1272
|
+
}
|
1273
|
+
}
|
1274
|
+
console.log(`Processing navigation breadcrumbs...`);
|
1275
|
+
const bc_build = hdoc.build_breadcrumbs(hdocbook_config.navigation.items);
|
1276
|
+
if (bc_build.errors.length > 0) {
|
1277
|
+
console.log("\r\n-----------------------");
|
1278
|
+
console.log(" Validation Output ");
|
1279
|
+
console.log("-----------------------");
|
1280
|
+
console.log(
|
1281
|
+
`\n${bc_build.errors.length} errors found when processing navigation:\n`
|
1282
|
+
);
|
1283
|
+
console.log(` - ${bc_build.errors.join("\n\n - ")}`);
|
1284
|
+
console.log("\n");
|
1285
|
+
process.exit(1);
|
1286
|
+
}
|
1287
|
+
bc = bc_build.bc;
|
1288
|
+
console.log(`Processing content...`);
|
1289
|
+
// Get a list of MD files in work_path
|
1290
|
+
dree.scan(work_path, dreeOptions, build_file_callback);
|
1291
|
+
|
1292
|
+
if (pdf_enable) {
|
1293
|
+
// Create a Chromium browser instance generate PDFs with
|
1294
|
+
browser = await puppeteer.launch({ headless: "new" });
|
1295
|
+
}
|
996
1296
|
|
997
|
-
|
998
|
-
|
1297
|
+
// Work through MD files and convert to HTML
|
1298
|
+
let mdPromiseArray = [];
|
1299
|
+
for (let i = 0; i < md_files.length; i++) {
|
1300
|
+
mdPromiseArray.push(md_files[i]);
|
1301
|
+
}
|
1302
|
+
const chunkSize = 8;
|
1303
|
+
for (let i = 0; i < mdPromiseArray.length; i += chunkSize) {
|
1304
|
+
const chunk = mdPromiseArray.slice(i, i + chunkSize);
|
1305
|
+
// do whatever
|
1306
|
+
await Promise.all(
|
1307
|
+
chunk.map(async (file) => {
|
1308
|
+
await transform_markdown_and_save_html(file);
|
1309
|
+
})
|
1310
|
+
);
|
1311
|
+
}
|
999
1312
|
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
try {
|
1014
|
-
fs.writeFileSync(work_hdocbook_path, JSON.stringify(hdocbook_config, null, 2));
|
1015
|
-
console.log('\nhdocbook.json update success:', work_hdocbook_path);
|
1016
|
-
} catch (e) {
|
1017
|
-
console.log('\nError creating', work_hdocbook_path, ':', e);
|
1018
|
-
process.exit(1);
|
1019
|
-
}
|
1313
|
+
// Work through Static HTML files and add Frontmatter tags
|
1314
|
+
let htmlPromiseArray = [];
|
1315
|
+
for (let i = 0; i < static_html_files.length; i++) {
|
1316
|
+
htmlPromiseArray.push(static_html_files[i]);
|
1317
|
+
}
|
1318
|
+
for (let i = 0; i < htmlPromiseArray.length; i += chunkSize) {
|
1319
|
+
const chunk = htmlPromiseArray.slice(i, i + chunkSize);
|
1320
|
+
await Promise.all(
|
1321
|
+
chunk.map(async (file) => {
|
1322
|
+
await transform_static_html(file);
|
1323
|
+
})
|
1324
|
+
);
|
1325
|
+
}
|
1020
1326
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
if (db.error && db.error !== null) {
|
1026
|
-
console.log(db.error);
|
1027
|
-
process.exit(1);
|
1028
|
-
}
|
1029
|
-
// Populate primary index tables
|
1030
|
-
const index = await hdoc_build_db.populate_index(db.db, doc_id, hdocbook_config, index_records, verbose);
|
1031
|
-
if (!index.success) {
|
1032
|
-
console.log(index.error);
|
1033
|
-
process.exit(1);
|
1034
|
-
}
|
1327
|
+
if (pdf_enable) {
|
1328
|
+
// Close the Chromium browser instance
|
1329
|
+
await browser.close();
|
1330
|
+
}
|
1035
1331
|
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1332
|
+
// Output to console
|
1333
|
+
console.log(`\n MD files found: ${conversion_attempted}`);
|
1334
|
+
console.log(`Successfully converted to HTML: ${conversion_success}`);
|
1335
|
+
console.log(` Failed to convert: ${conversion_failed}\n`);
|
1336
|
+
console.log(` Includes Found: ${includes_found}`);
|
1337
|
+
console.log(` Includes Success: ${includes_success}`);
|
1338
|
+
console.log(` Includes Failed: ${includes_failed}\n`);
|
1339
|
+
console.log(
|
1340
|
+
` Static HTML Files Found: ${static_html_files.length}\n`
|
1341
|
+
);
|
1342
|
+
if (!validate) {
|
1343
|
+
console.log(` PDF Files Created: ${pdf_created}\n`);
|
1344
|
+
}
|
1046
1345
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1346
|
+
// Validate content
|
1347
|
+
const validation_success = await hdoc_validate.run(
|
1348
|
+
work_path,
|
1349
|
+
doc_id,
|
1350
|
+
verbose,
|
1351
|
+
hdocbook_config,
|
1352
|
+
hdocbook_project,
|
1353
|
+
bc,
|
1354
|
+
prod_families,
|
1355
|
+
prods_supported,
|
1356
|
+
gen_exclude,
|
1357
|
+
redirects
|
1358
|
+
);
|
1359
|
+
if (!validation_success) {
|
1360
|
+
const end_time = Date.now();
|
1361
|
+
console.log(`\nTime Taken: ${get_duration(start_time, end_time)}\n`);
|
1362
|
+
process.exit(1);
|
1363
|
+
}
|
1050
1364
|
|
1051
|
-
|
1052
|
-
|
1053
|
-
archive.on('error', function(err){
|
1054
|
-
throw err;
|
1055
|
-
});
|
1056
|
-
archive.pipe(output);
|
1365
|
+
// Delete markdown files
|
1366
|
+
console.log(`Performing Markdown Cleanup`);
|
1057
1367
|
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1368
|
+
let filePromiseArray = [];
|
1369
|
+
for (let i = 0; i < md_files_delete.length; i++) {
|
1370
|
+
filePromiseArray.push(md_files_delete[i]);
|
1371
|
+
}
|
1372
|
+
await Promise.all(
|
1373
|
+
filePromiseArray.map(async (file) => {
|
1374
|
+
fs.unlink(file, (err) => {
|
1375
|
+
if (err) console.log(`Error deleting ${file}: ${e}`);
|
1376
|
+
});
|
1377
|
+
})
|
1378
|
+
);
|
1379
|
+
|
1380
|
+
// Add book read timing to the hdocbook.json
|
1381
|
+
hdocbook_config.readingTime = Math.ceil(
|
1382
|
+
book_read_time + (book_read_time / 100) * 10
|
1383
|
+
);
|
1384
|
+
hdocbook_config.navigation.items = hdoc.strip_drafts(
|
1385
|
+
hdocbook_config.navigation.items
|
1386
|
+
);
|
1387
|
+
try {
|
1388
|
+
fs.writeFileSync(
|
1389
|
+
work_hdocbook_path,
|
1390
|
+
JSON.stringify(hdocbook_config, null, 2)
|
1391
|
+
);
|
1392
|
+
console.log("\nhdocbook.json update success:", work_hdocbook_path);
|
1393
|
+
} catch (e) {
|
1394
|
+
console.log("\nError creating", work_hdocbook_path, ":", e);
|
1395
|
+
process.exit(1);
|
1396
|
+
}
|
1061
1397
|
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1398
|
+
// Build the index
|
1399
|
+
// Create the DB and tables
|
1400
|
+
console.log(`Building the Index`);
|
1401
|
+
let db = hdoc_build_db.create_db(work_path, doc_id);
|
1402
|
+
if (db.error && db.error !== null) {
|
1403
|
+
console.log(db.error);
|
1404
|
+
process.exit(1);
|
1405
|
+
}
|
1406
|
+
// Populate primary index tables
|
1407
|
+
const index = await hdoc_build_db.populate_index(
|
1408
|
+
db.db,
|
1409
|
+
doc_id,
|
1410
|
+
hdocbook_config,
|
1411
|
+
index_records,
|
1412
|
+
verbose
|
1413
|
+
);
|
1414
|
+
if (!index.success) {
|
1415
|
+
console.log(index.error);
|
1416
|
+
process.exit(1);
|
1417
|
+
}
|
1066
1418
|
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
if (parseInt(duration_arr[0], 10) > 0) {
|
1082
|
-
duration += parseInt(duration_arr[0], 10) + 'h ';
|
1419
|
+
// Populate redirect index table records
|
1420
|
+
if (
|
1421
|
+
hdocbook_project.redirects &&
|
1422
|
+
hdocbook_project.redirects instanceof Array &&
|
1423
|
+
hdocbook_project.redirects.length > 0
|
1424
|
+
) {
|
1425
|
+
const redirects_index = hdoc_build_db.populate_redirects(
|
1426
|
+
db.db,
|
1427
|
+
hdocbook_project.redirects,
|
1428
|
+
verbose
|
1429
|
+
);
|
1430
|
+
if (!redirects_index.success) {
|
1431
|
+
for (let i = 0; i < index.errors.length; i++) {
|
1432
|
+
console.log(index.errors[i]);
|
1083
1433
|
}
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1434
|
+
process.exit(1);
|
1435
|
+
}
|
1436
|
+
}
|
1437
|
+
|
1438
|
+
if (!validate) {
|
1439
|
+
try {
|
1440
|
+
const zip_path = path.join(work_path, doc_id + ".zip");
|
1441
|
+
|
1442
|
+
var output = fs.createWriteStream(zip_path);
|
1443
|
+
var archive = archiver("zip");
|
1444
|
+
archive.on("error", function (err) {
|
1445
|
+
throw err;
|
1446
|
+
});
|
1447
|
+
archive.pipe(output);
|
1448
|
+
|
1449
|
+
// append files from a sub-directory, putting its contents at the root of archive
|
1450
|
+
archive.directory(work_path_content, false);
|
1451
|
+
archive.finalize();
|
1452
|
+
|
1453
|
+
//await zip(work_path_content, zip_path);
|
1454
|
+
console.log(`\nZIP Creation Success: ${zip_path}\n`);
|
1455
|
+
console.log(" Build Started:", build_start_dt);
|
1456
|
+
console.log(`Build Completed: ${new Date().toLocaleString()}\n`);
|
1457
|
+
} catch (e) {
|
1458
|
+
console.log("\nError creating ZIP: " + e);
|
1459
|
+
}
|
1460
|
+
} else {
|
1461
|
+
console.log("\nValidation Complete\n");
|
1462
|
+
}
|
1463
|
+
const end_time = Date.now();
|
1464
|
+
console.log(`Time Taken: ${get_duration(start_time, end_time)}\n`);
|
1465
|
+
};
|
1466
|
+
|
1467
|
+
const get_duration = function (start, end) {
|
1468
|
+
const total_time = new Date(end - start).toISOString().slice(11, 19);
|
1469
|
+
const duration_arr = total_time.split(":");
|
1470
|
+
let duration = "";
|
1471
|
+
if (parseInt(duration_arr[0], 10) > 0) {
|
1472
|
+
duration += parseInt(duration_arr[0], 10) + "h ";
|
1473
|
+
}
|
1474
|
+
if (duration !== "" || parseInt(duration_arr[1], 10)) {
|
1475
|
+
duration += parseInt(duration_arr[1], 10) + "m ";
|
1089
1476
|
}
|
1477
|
+
duration += parseInt(duration_arr[2], 10) + "s";
|
1478
|
+
return duration;
|
1479
|
+
};
|
1090
1480
|
})();
|