docgen-tool 3.0.0 → 3.0.2

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.
Files changed (77) hide show
  1. package/dist/docgen.js +4 -4
  2. package/dist/source/docgen.js +891 -0
  3. package/package.json +9 -2
  4. package/.gitattributes +0 -1
  5. package/.github/workflows/deploy-website.yml +0 -28
  6. package/.prettierignore +0 -4
  7. package/.prettierrc +0 -4
  8. package/docgen.js +0 -95
  9. package/index.html +0 -10
  10. package/source/__test__/test-run/contents.json +0 -12
  11. package/source/__test__/test-run/files/images/logo.png +0 -0
  12. package/source/__test__/test-run/overview.txt +0 -3
  13. package/source/__test__/test-run/parameters.json +0 -32
  14. package/source/__test__/test-run/release-notes.txt +0 -4
  15. package/source/docgen.js +0 -988
  16. package/source/example/contents.json +0 -12
  17. package/source/example/files/images/logo.png +0 -0
  18. package/source/example/index.txt +0 -4
  19. package/source/example/parameters.json +0 -37
  20. package/source/example/release-notes.txt +0 -1
  21. package/source/internal-readme.md +0 -4
  22. package/source/optional/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  23. package/source/optional/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  24. package/source/optional/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  25. package/source/optional/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  26. package/source/optional/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  27. package/source/optional/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  28. package/source/optional/katex/fonts/KaTeX_Math-Regular.woff +0 -0
  29. package/source/optional/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  30. package/source/optional/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  31. package/source/optional/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  32. package/source/optional/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  33. package/source/optional/katex/katex.min.css +0 -1
  34. package/source/optional/katex/katex.min.js +0 -6
  35. package/source/pdf-contents.xsl +0 -71
  36. package/source/pdf-stylesheet.css +0 -59
  37. package/source/release-checklist.txt +0 -27
  38. package/source/require/docgen.css +0 -138
  39. package/source/require/katexInjector.js +0 -18
  40. package/source/require/print.css +0 -26
  41. package/source/require/webknife/fonts/DroidSansMono.woff +0 -0
  42. package/source/require/webknife/fonts/OpenSans.woff +0 -0
  43. package/source/require/webknife/fonts/OpenSansBold.woff +0 -0
  44. package/source/require/webknife/fonts/OpenSansBoldItalic.woff +0 -0
  45. package/source/require/webknife/fonts/OpenSansItalic.woff +0 -0
  46. package/source/require/webknife/framework.icons.js +0 -82
  47. package/source/require/webknife/framework.min.css +0 -3
  48. package/source/require/webknife/framework.min.js +0 -14
  49. package/source/require/webknife/highlight.min.css +0 -1
  50. package/source/require/webknife/highlight.min.js +0 -6
  51. package/source/templates/main.html +0 -93
  52. package/source/templates/pdfCover.html +0 -128
  53. package/source/templates/pdfFooter.html +0 -59
  54. package/source/templates/pdfHeader.html +0 -21
  55. package/source/templates/redirect.html +0 -10
  56. package/source/templates/webCover.html +0 -73
  57. package/source/user-guide/advanced-content.txt +0 -171
  58. package/source/user-guide/commonmark.txt +0 -156
  59. package/source/user-guide/contents.json +0 -57
  60. package/source/user-guide/files/images/logo.png +0 -0
  61. package/source/user-guide/files/images/overview.png +0 -0
  62. package/source/user-guide/files/images/pdf.png +0 -0
  63. package/source/user-guide/files/images/svg/icon.svg +0 -845
  64. package/source/user-guide/files/images/svg/inkit-logo.svg +0 -9
  65. package/source/user-guide/files/images/svg/overview.svg +0 -1345
  66. package/source/user-guide/files/images/text.png +0 -0
  67. package/source/user-guide/files/images/web.png +0 -0
  68. package/source/user-guide/index.txt +0 -258
  69. package/source/user-guide/installation.txt +0 -49
  70. package/source/user-guide/parameters.json +0 -37
  71. package/source/user-guide/release-notes.txt +0 -70
  72. package/source/user-guide/running.txt +0 -47
  73. package/source/user-guide/troubleshooting.txt +0 -32
  74. package/source/user-guide/upgrading.txt +0 -25
  75. package/source/user-guide/version-control.txt +0 -13
  76. package/source/user-guide/writing-content.txt +0 -269
  77. package/tsconfig.json +0 -8
package/source/docgen.js DELETED
@@ -1,988 +0,0 @@
1
- const rsvp = require('rsvp');
2
- const fs = require('fs-extra');
3
- const path = require('path');
4
- const cheerio = require('cheerio');
5
- const markdown = require('markdown-it')('commonmark').enable('table');
6
- const moment = require('moment');
7
- import { spawn, exec } from 'child_process';
8
- const schemaValidator = require('z-schema');
9
- const chalk = require('chalk');
10
- const spawnArgs = require('spawn-args');
11
- const cliSpinner = require('cli-spinner').Spinner;
12
- const imageSizeOf = require('image-size');
13
-
14
- //Allow CommonMark links that use other protocols, such as file:///
15
- //The markdown-it implementation is more restrictive than the CommonMark spec
16
- //See https://github.com/markdown-it/markdown-it/issues/108
17
- markdown.validateLink = () => {
18
- return true;
19
- };
20
-
21
- /**
22
- * DocGen class
23
- */
24
-
25
- function DocGen(process) {
26
- let mainProcess = process;
27
- let version = '3.0.0';
28
- let wkhtmltopdfVersion = 'wkhtmltopdf 0.12.6 (with patched qt)'; //output from wkhtmltopdf -V
29
- let options;
30
- let templates = {};
31
- let meta = {};
32
- let pages = {};
33
- let sortedPages = {};
34
-
35
- this.getVersion = () => {
36
- return version;
37
- };
38
-
39
- this.setOptions = (userOptions) => {
40
- options = userOptions;
41
- //all user-specified paths must be normalized
42
- if (options.input) {
43
- options.input = path.normalize(options.input + '/');
44
- }
45
- if (options.output) {
46
- options.output = path.normalize(options.output + '/');
47
- }
48
-
49
- //wkhtmltopdf path does not need a trailing slash
50
- if (options.wkhtmltopdfPath && options.wkhtmltopdfPath !== '') {
51
- options.wkhtmltopdfPath = path.normalize(options.wkhtmltopdfPath);
52
- }
53
- };
54
-
55
- /*
56
- copy the example source files (template) to any directory, when scaffold command is invoked
57
- */
58
-
59
- this.scaffold = () => {
60
- console.log(chalk.green('Creating scaffold template directory'));
61
- copyDirSync(__dirname + '/example', options.output);
62
- };
63
-
64
- this.run = () => {
65
- console.log(chalk.green.bold('DocGen version ' + version));
66
- //delete and recreate the output directory
67
- remakeDirSync(options.output);
68
- loadTemplates();
69
- };
70
-
71
- /*
72
- read any file (async)
73
- */
74
-
75
- let readFile = (path) => {
76
- return new rsvp.Promise((resolve, reject) => {
77
- fs.readFile(path, 'utf8', (error, data) => {
78
- if (error) {
79
- console.log(chalk.red('Error reading file: ' + path));
80
- reject(error);
81
- } else {
82
- data = data.replace(/^\uFEFF/, ''); //remove the BOM (byte-order-mark) from UTF-8 files, if present
83
- resolve(data);
84
- }
85
- });
86
- });
87
- };
88
-
89
- /*
90
- write any file (async)
91
- */
92
-
93
- let writeFile = (path, data) => {
94
- return new rsvp.Promise((resolve, reject) => {
95
- fs.writeFile(path, data, (error) => {
96
- if (error) {
97
- console.log(chalk.red('Error writing file: ' + path));
98
- reject(error);
99
- } else {
100
- resolve(true);
101
- }
102
- });
103
- });
104
- };
105
-
106
- /*
107
- copy any directory (sync)
108
- */
109
-
110
- let copyDirSync = (source, destination) => {
111
- try {
112
- fs.copySync(source, destination);
113
- } catch (error) {
114
- console.log(
115
- chalk.red('Error copying directory: ' + source + ' to ' + destination),
116
- );
117
- if (options.verbose === true) {
118
- console.log(chalk.red(error));
119
- mainProcess.exit(1);
120
- }
121
- }
122
- };
123
-
124
- /*
125
- remake a directory (sync) ... remove and then mkdir in one operation
126
- */
127
-
128
- let remakeDirSync = (path) => {
129
- try {
130
- fs.removeSync(path);
131
- fs.mkdirpSync(path);
132
- } catch (error) {
133
- console.log(chalk.red('Error recreating directory: ' + path));
134
- if (options.verbose === true) {
135
- console.log(chalk.red(error));
136
- mainProcess.exit(1);
137
- }
138
- }
139
- };
140
-
141
- /*
142
- remove any directory (sync)
143
- */
144
-
145
- let removeDirSync = (path) => {
146
- try {
147
- fs.removeSync(path);
148
- } catch (error) {
149
- console.log(chalk.red('Error removing directory: ' + path));
150
- if (options.verbose === true) {
151
- console.log(chalk.red(error));
152
- mainProcess.exit(1);
153
- }
154
- }
155
- };
156
-
157
- /*
158
- load all HTML template files
159
- */
160
-
161
- let loadTemplates = () => {
162
- console.log(chalk.green('Loading templates'));
163
- let files = {
164
- main: readFile(__dirname + '/templates/main.html'),
165
- redirect: readFile(__dirname + '/templates/redirect.html'),
166
- webCover: readFile(__dirname + '/templates/webCover.html'),
167
- pdfCover: readFile(__dirname + '/templates/pdfCover.html'),
168
- pdfHeader: readFile(__dirname + '/templates/pdfHeader.html'),
169
- pdfFooter: readFile(__dirname + '/templates/pdfFooter.html'),
170
- };
171
- rsvp
172
- .hash(files)
173
- .then((files) => {
174
- for (let key in files) {
175
- if (files.hasOwnProperty(key)) {
176
- let file = files[key];
177
- let dom = cheerio.load(file);
178
- templates[key] = dom;
179
- }
180
- }
181
- loadMeta();
182
- })
183
- .catch((error) => {
184
- console.log(chalk.red('Error loading templates'));
185
- if (options.verbose === true) {
186
- console.log(chalk.red(error));
187
- }
188
- mainProcess.exit(1);
189
- });
190
- };
191
-
192
- /*
193
- JSON schema validation
194
- */
195
-
196
- let schemas = {
197
- parameters: {
198
- title: 'DocGen Parameters Schema',
199
- type: 'object',
200
- required: [
201
- 'title',
202
- 'name',
203
- 'version',
204
- 'date',
205
- 'organization',
206
- 'author',
207
- 'owner',
208
- 'contributors',
209
- 'website',
210
- 'module',
211
- 'id',
212
- 'summary',
213
- 'marking',
214
- 'legalese',
215
- ],
216
- properties: {
217
- title: { type: 'string' },
218
- name: { type: 'string' },
219
- version: { type: 'string' },
220
- date: { type: 'string' },
221
- organization: {
222
- type: 'object',
223
- required: ['name', 'url'],
224
- properties: {
225
- name: { type: 'string' },
226
- url: { type: 'string' },
227
- },
228
- },
229
- author: {
230
- type: 'object',
231
- required: ['name', 'url'],
232
- properties: {
233
- name: { type: 'string' },
234
- url: { type: 'string' },
235
- },
236
- },
237
- owner: {
238
- type: 'object',
239
- required: ['name', 'url'],
240
- properties: {
241
- name: { type: 'string' },
242
- url: { type: 'string' },
243
- },
244
- },
245
- contributors: {
246
- type: 'array',
247
- items: {
248
- oneOf: [
249
- {
250
- type: 'object',
251
- required: ['name', 'url'],
252
- properties: {
253
- name: { type: 'string' },
254
- url: { type: 'string' },
255
- },
256
- },
257
- ],
258
- },
259
- },
260
- website: {
261
- type: 'object',
262
- required: ['name', 'url'],
263
- properties: {
264
- name: { type: 'string' },
265
- url: { type: 'string' },
266
- },
267
- },
268
- backlink: {
269
- type: 'object',
270
- required: ['name', 'url'],
271
- properties: {
272
- name: { type: 'string' },
273
- url: { type: 'string' },
274
- },
275
- },
276
- module: { type: 'string' },
277
- id: { type: 'string' },
278
- summary: { type: 'string' },
279
- marking: { type: 'string' },
280
- legalese: { type: 'string' },
281
- },
282
- },
283
- contents: {
284
- title: 'DocGen Table of Contents Schema',
285
- type: 'array',
286
- items: {
287
- oneOf: [
288
- {
289
- type: 'object',
290
- required: ['heading', 'column', 'pages'],
291
- properties: {
292
- name: { type: 'string' },
293
- column: { type: 'integer', minimum: 1, maximum: 4 },
294
- pages: {
295
- type: 'array',
296
- items: {
297
- oneOf: [
298
- {
299
- type: 'object',
300
- required: ['title', 'source'],
301
- properties: {
302
- title: { type: 'string' },
303
- source: { type: 'string' },
304
- html: { type: 'boolean' },
305
- },
306
- },
307
- ],
308
- },
309
- },
310
- },
311
- },
312
- ],
313
- },
314
- },
315
- };
316
-
317
- let validateJSON = (key, data) => {
318
- let schema = schemas[key];
319
- let validator = new schemaValidator();
320
- let valid = validator.validate(data, schema);
321
- if (!valid) {
322
- console.log(
323
- chalk.red(
324
- 'Error parsing required file: ' +
325
- key +
326
- '.json (failed schema validation)',
327
- ),
328
- );
329
- if (options.verbose === true) {
330
- console.log(chalk.red(validator.getLastError()));
331
- }
332
- }
333
- return valid;
334
- };
335
-
336
- /*
337
- load all metadata files (JSON)
338
- */
339
-
340
- let loadMeta = () => {
341
- console.log(chalk.green('Loading required JSON metadata files'));
342
- let files = {
343
- parameters: readFile(options.input + '/parameters.json'),
344
- contents: readFile(options.input + '/contents.json'),
345
- };
346
- rsvp
347
- .hash(files)
348
- .then((files) => {
349
- for (let key in files) {
350
- if (files.hasOwnProperty(key)) {
351
- //ignore prototype
352
- try {
353
- let file = JSON.parse(files[key]);
354
- if (validateJSON(key, file)) {
355
- meta[key] = file;
356
- } else {
357
- mainProcess.exit(1);
358
- }
359
- } catch (error) {
360
- console.log(
361
- chalk.red(
362
- 'Error parsing required file: ' +
363
- key +
364
- '.json (invalid JSON)',
365
- ),
366
- );
367
- if (options.verbose === true) {
368
- console.log(chalk.red(error));
369
- }
370
- mainProcess.exit(1);
371
- }
372
- }
373
- }
374
- //add the release notes to the contents list
375
- let extra = {
376
- heading: 'Extra',
377
- column: 5,
378
- pages: [{ title: 'Release notes', source: 'release-notes.txt' }],
379
- };
380
- meta.contents.push(extra);
381
- loadMarkdown();
382
- })
383
- .catch((error) => {
384
- console.log(chalk.red('Error loading required JSON metadata files'));
385
- if (options.verbose === true) {
386
- console.log(chalk.red(error));
387
- }
388
- mainProcess.exit(1);
389
- });
390
- };
391
-
392
- /*
393
- load all markdown files (source)
394
- */
395
-
396
- let loadMarkdown = () => {
397
- console.log(chalk.green('Loading source files'));
398
- let keys = [];
399
- let files = [];
400
- meta.contents.forEach((section) => {
401
- section.pages.forEach((page) => {
402
- keys.push(page);
403
- files.push(options.input + '/' + page.source);
404
- });
405
- });
406
- //add the release notes page
407
- keys.push('ownership');
408
- files.push(options.input + '/release-notes.txt');
409
- rsvp
410
- .all(files.map(readFile))
411
- .then((files) => {
412
- files.forEach((page, index) => {
413
- try {
414
- let key = keys[index];
415
- if (key.html === true) {
416
- //allow raw HTML input pages
417
- pages[key.source] = page;
418
- } else {
419
- //otherwise parse input from Markdown into HTML
420
- let html = markdown.render(page);
421
- pages[key.source] = html;
422
- }
423
- } catch (error) {
424
- console.log(
425
- chalk.red('Error parsing Markdown file: ' + file.source),
426
- );
427
- if (options.verbose === true) {
428
- console.log(chalk.red(error));
429
- }
430
- mainProcess.exit(1);
431
- }
432
- });
433
- processContent();
434
- })
435
- .catch((error) => {
436
- console.log(error);
437
- console.log(chalk.red('Error loading source files'));
438
- if (options.verbose === true) {
439
- console.log(chalk.red(error));
440
- }
441
- mainProcess.exit(1);
442
- });
443
- };
444
-
445
- let sortPages = () => {
446
- //sort the contents by heading
447
- let headings = { 1: [], 2: [], 3: [], 4: [], 5: [] };
448
- meta.contents.forEach((section) => {
449
- if (headings.hasOwnProperty(section.column)) {
450
- headings[section.column].push(section);
451
- }
452
- });
453
- sortedPages = headings;
454
- };
455
-
456
- /*
457
- build the HTML for the table of contents
458
- */
459
-
460
- let webToc = () => {
461
- sortPages();
462
- let pdfName = meta.parameters.name.toLowerCase() + '.pdf';
463
- let $ = templates.main;
464
- let html = [],
465
- i = -1;
466
- html[++i] = '<div><table class="unstyled"><tr>';
467
- //build the contents HTML
468
- for (let key in sortedPages) {
469
- if (sortedPages.hasOwnProperty(key)) {
470
- if (key != 5) {
471
- //skip the extra column
472
- html[++i] = '<td class="dg-tocGroup">';
473
- sortedPages[key].forEach((section) => {
474
- html[++i] =
475
- '<ul><li class="dg-tocHeading">' + section.heading + '</li>';
476
- section.pages.forEach((page) => {
477
- let name = page.source.substr(0, page.source.lastIndexOf('.'));
478
- let path = name + '.html';
479
- html[++i] =
480
- '<li><a href="' + path + '">' + page.title + '</a></li>';
481
- });
482
- html[++i] = '</li></ul>';
483
- });
484
- html[++i] = '</td>';
485
- }
486
- }
487
- }
488
-
489
- //fixed-width column at end
490
- html[++i] = '<td class="dg-tocGroup" id="dg-tocFixedColumn"><ul>';
491
- html[++i] =
492
- '<li><span class="w-icon dg-tocIcon" data-name="person_group" title="archive"></span><a href="ownership.html">Ownership</a></li>';
493
- html[++i] =
494
- '<li><span class="w-icon dg-tocIcon" data-name="refresh" title="archive"></span><a href="release-notes.html">Release Notes</a></li>';
495
- html[++i] = '</ul><div>';
496
- if (options.pdf) {
497
- html[++i] =
498
- '<button class="w-icon-button" onclick="window.location=\'' +
499
- pdfName +
500
- '\';">';
501
- html[++i] = '<span class="w-icon" data-name="document"></span>';
502
- html[++i] = '<span>PDF copy</span>';
503
- html[++i] = '</button>';
504
- }
505
- html[++i] = '</div></td>';
506
- html[++i] = '</tr></table></div>';
507
- $('#dg-toc').html(html.join(''));
508
- templates.main = $;
509
- };
510
-
511
- /*
512
- insert the parameters into all templates
513
- */
514
-
515
- let insertParameters = () => {
516
- //------------------------------------------------------------------------------------------------------
517
- //logo dimensions
518
- let hasLogo = false;
519
- let logoWidth = 0;
520
- let logoHeight = 0;
521
- try {
522
- let logo = imageSizeOf(options.input + '/files/images/logo.png');
523
- logoWidth = logo.width;
524
- logoHeight = logo.height;
525
- hasLogo = true;
526
- } catch (error) {
527
- //do nothing. If logo file cannot be read, logo is simply not shown
528
- }
529
-
530
- //------------------------------------------------------------------------------------------------------
531
-
532
- //the homepage is the first link in the first heading
533
- let homelink = meta.contents[0].pages[0];
534
- homelink =
535
- homelink.source.substr(0, homelink.source.lastIndexOf('.')) + '.html';
536
-
537
- let date = moment().format('DD/MM/YYYY');
538
- let time = moment().format('HH:mm:ss');
539
- let year = moment().format('YYYY');
540
- let attribution =
541
- 'Created by DocGen ' + version + ' on ' + date + ' at ' + time + '.';
542
-
543
- let releaseVersion = meta.parameters.version;
544
- if (options.setVersion !== false) {
545
- releaseVersion = options.setVersion;
546
- }
547
- let releaseDate = meta.parameters.date;
548
- if (options.setReleaseDate !== false) {
549
- releaseDate = options.setReleaseDate;
550
- }
551
-
552
- let author = '';
553
- if (meta.parameters.author.url !== '') {
554
- author +=
555
- '<a href="' +
556
- meta.parameters.author.url +
557
- '">' +
558
- meta.parameters.author.name +
559
- '</a>';
560
- } else {
561
- author += meta.parameters.author.name;
562
- }
563
-
564
- let owner = '';
565
- if (meta.parameters.owner.url !== '') {
566
- owner +=
567
- '<a href="' +
568
- meta.parameters.owner.url +
569
- '">' +
570
- meta.parameters.owner.name +
571
- '</a>';
572
- } else {
573
- owner += meta.parameters.owner.name;
574
- }
575
-
576
- let organization = '';
577
- if (meta.parameters.organization.url !== '') {
578
- organization +=
579
- '<a href="' +
580
- meta.parameters.organization.url +
581
- '">' +
582
- meta.parameters.organization.name +
583
- '</a>';
584
- } else {
585
- organization += meta.parameters.organization.name;
586
- }
587
-
588
- let website = '';
589
- if (meta.parameters.website.url !== '') {
590
- website +=
591
- '<a href="' +
592
- meta.parameters.website.url +
593
- '">' +
594
- meta.parameters.website.name +
595
- '</a>';
596
- } else {
597
- website += meta.parameters.website.name;
598
- }
599
-
600
- let backlink = '';
601
- if (meta.parameters.backlink.url !== '') {
602
- backlink +=
603
- '<a href="' +
604
- meta.parameters.backlink.url +
605
- '">' +
606
- meta.parameters.backlink.name +
607
- '</a>';
608
- } else {
609
- backlink += meta.parameters.backlink.name;
610
- }
611
-
612
- let contributors = '';
613
- meta.parameters.contributors.forEach((contributor) => {
614
- if (contributor.url !== '') {
615
- contributors +=
616
- '<a href="' + contributor.url + '">' + contributor.name + '</a>, ';
617
- } else {
618
- contributors += contributor.name + ', ';
619
- }
620
- });
621
- contributors = contributors.replace(/,\s*$/, ''); //remove trailing commas
622
-
623
- let copyright = '&copy; ' + year + ' ' + organization;
624
-
625
- let webTitle = meta.parameters.title;
626
-
627
- let webFooter =
628
- 'Version ' + releaseVersion + ' released on ' + releaseDate + '.';
629
-
630
- for (let key in templates) {
631
- if (templates.hasOwnProperty(key)) {
632
- let $ = templates[key];
633
- //logo
634
- if (hasLogo === true) {
635
- let logoUrl = 'files/images/logo.png';
636
- $('#dg-logo').css('background-image', 'url(' + logoUrl + ')');
637
- $('#dg-logo').css('height', logoHeight + 'px');
638
- $('#dg-logo').css('line-height', logoHeight + 'px');
639
- $('#dg-logo').css('padding-left', logoWidth + 25 + 'px');
640
- } else {
641
- $('#dg-logo').css('padding-left', '0');
642
- }
643
- //parameters
644
- $('title').text(meta.parameters.title);
645
- $('#dg-homelink').attr('href', homelink);
646
- $('#dg-title').text(meta.parameters.title);
647
- $('#dg-owner').html(owner);
648
- $('#dg-version').text(releaseVersion);
649
- $('#dg-web-title-version').text('(' + releaseVersion + ')');
650
- $('#dg-release-date').text(releaseDate);
651
- $('#dg-web-footer').text(webFooter);
652
- $('#dg-author').html(author);
653
- $('#dg-contributors').html(contributors);
654
- $('#dg-module').text(meta.parameters.module);
655
- $('#dg-id').html(meta.parameters.id);
656
- $('#dg-website').html(website);
657
- $('#dg-backlink').html(backlink);
658
- $('#dg-summary').text(meta.parameters.summary);
659
- $('#dg-copyright').html(copyright);
660
- $('#dg-marking').text(meta.parameters.marking);
661
- $('#dg-legalese').text(meta.parameters.legalese);
662
- $('#dg-attribution').text(attribution);
663
- }
664
- }
665
- if (options.mathKatex === true) {
666
- let $ = templates.main;
667
- //support for KaTeX (bundled with DocGen)
668
- $('head').append(
669
- '<link rel="stylesheet" href="require/katex/katex.min.css" type="text/css">',
670
- );
671
- $('head').append(
672
- '<script type="text/javascript" src="require/katex/katex.min.js"></script>',
673
- );
674
- $('head').append(
675
- '<script type="text/javascript" src="require/katexInjector.js"></script>',
676
- );
677
- }
678
- if (options.mathMathjax === true) {
679
- //support for MathJax (only supported via CDN due to very large size)
680
- //MathJax configuration is the same as used by math.stackexchange.com
681
- //Note - wkhtmlpdf //cdn urls - see https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1634
682
- $('head').append(
683
- '<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full"></script>',
684
- );
685
- }
686
- };
687
-
688
- /*
689
- process each input into an output
690
- */
691
-
692
- let processContent = () => {
693
- console.log(chalk.green('Generating the static web content'));
694
- webToc();
695
- insertParameters();
696
- meta.contents.forEach((section) => {
697
- section.pages.forEach((page) => {
698
- let $ = cheerio.load(templates.main.html()); //clone
699
- let key = page.source;
700
- let content = pages[key];
701
- //add relevant container
702
- if (page.html === true) {
703
- //raw HTML pages should not be confined to the fixed width
704
- $('#dg-content').html('<div id="dg-innerContent"></div>');
705
- } else {
706
- //Markdown pages should be confined to the fixed width
707
- $('#dg-content').html(
708
- '<div class="w-fixed-width"><div id="dg-innerContent"></div></div>',
709
- );
710
- }
711
- $('#dg-innerContent').html(content);
712
- //------------------------------------------------------------------------------------------------------
713
- //insert permalinks for every page heading
714
- //when pageToc is enabled, also insert a page-level table of contents
715
- let html = [],
716
- i = -1;
717
- let headings = $('h1, h2, h3, h4, h5, h6');
718
- if (headings.length > 0) {
719
- html[++i] = '<ul class="dg-pageToc">';
720
- }
721
- headings.each(function () {
722
- let label = $(this).text();
723
- let anchor = label.toLowerCase().replace(/\s+/g, '-');
724
- $(this).attr('id', anchor);
725
- html[++i] = '<li><a href="#' + anchor + '">' + label + '</a></li>';
726
- });
727
- if (headings.length > 0) {
728
- html[++i] = '</ul>';
729
- }
730
- if (options.pageToc === true && page.html !== true) {
731
- $('#dg-innerContent').prepend(html.join(''));
732
- }
733
- //------------------------------------------------------------------------------------------------------
734
- //prepend the auto heading (which makes the PDF table of contents match the web TOC)
735
- $('#dg-innerContent').prepend(
736
- '<h1 id="dg-autoTitle">' + page.title + '</h1>',
737
- );
738
- if (page.html === true) {
739
- $('#dg-autoTitle').addClass('dg-hiddenTitle');
740
- }
741
- //------------------------------------------------------------------------------------------------------
742
- //apply the w-table class
743
- $('table:not(.unstyled)').addClass('w-table w-fixed w-stripe');
744
- //------------------------------------------------------------------------------------------------------
745
- pages[key] = $;
746
- });
747
- });
748
- //add web ownership page
749
- let $ = cheerio.load(templates.main.html()); //clone
750
- $('#dg-content').html(
751
- '<div class="w-fixed-width"><div id="dg-innerContent"></div></div>',
752
- );
753
- $('#dg-innerContent').html(templates.webCover.html());
754
- templates.webCover = $;
755
- writePages();
756
- };
757
-
758
- /*
759
- write each html page
760
- */
761
-
762
- let writePages = () => {
763
- console.log(chalk.green('Writing the web page files'));
764
- let promises = {};
765
- meta.contents.forEach((section) => {
766
- section.pages.forEach((page) => {
767
- let key = page.source;
768
- let name = key.substr(0, page.source.lastIndexOf('.'));
769
- let path = options.output + name + '.html';
770
- let html = pages[key].html();
771
- promises[key] = writeFile(path, html);
772
- });
773
- });
774
- //add extra files
775
- promises['ownership'] = writeFile(
776
- options.output + 'ownership.html',
777
- templates.webCover.html(),
778
- );
779
- if (options.pdf === true) {
780
- let pdfTempDir = options.output + 'temp/';
781
- fs.mkdirsSync(pdfTempDir);
782
- promises['docgenPdfCover'] = writeFile(
783
- pdfTempDir + 'pdfCover.html',
784
- templates.pdfCover.html(),
785
- );
786
- promises['docgenPdfHeader'] = writeFile(
787
- pdfTempDir + 'pdfHeader.html',
788
- templates.pdfHeader.html(),
789
- );
790
- promises['docgenPdfFooter'] = writeFile(
791
- pdfTempDir + 'pdfFooter.html',
792
- templates.pdfFooter.html(),
793
- );
794
- }
795
- rsvp
796
- .hash(promises)
797
- .then(() => {
798
- copyDirSync(__dirname + '/require', options.output + 'require'); //CSS, JavaScript
799
- copyDirSync(options.input + '/files', options.output + 'files'); //user-attached files and images
800
- if (options.mathKatex === true) {
801
- copyDirSync(
802
- __dirname + '/optional/katex',
803
- options.output + 'require/katex',
804
- );
805
- }
806
- checkPdfVersion();
807
- })
808
- .catch((error) => {
809
- console.log(chalk.red('Error writing the web page files'));
810
- if (options.verbose === true) {
811
- console.log(chalk.red(error));
812
- }
813
- mainProcess.exit(1);
814
- });
815
- };
816
-
817
- /*
818
- wkthmltopdf options
819
- */
820
-
821
- let pdfOptions = [
822
- ' --zoom 1.0',
823
- ' --image-quality 100',
824
- ' --print-media-type',
825
- ' --orientation portrait',
826
- ' --page-size A4',
827
- ' --margin-top 25',
828
- ' --margin-right 15',
829
- ' --margin-bottom 16',
830
- ' --margin-left 15',
831
- ' --header-spacing 5',
832
- ' --footer-spacing 5',
833
- ' --no-stop-slow-scripts',
834
- ];
835
-
836
- let getPdfArguments = () => {
837
- let pdfName = meta.parameters.name.toLowerCase() + '.pdf';
838
- pdfOptions.push(' --enable-local-file-access');
839
- pdfOptions.push(' --javascript-delay ' + options.pdfDelay); //code syntax highlight in wkhtmltopdf 0.12.2.1 fails without a delay (but why doesn't --no-stop-slow-scripts work?)
840
- pdfOptions.push(' --user-style-sheet ' + __dirname + '/pdf-stylesheet.css');
841
- pdfOptions.push(' --header-html ' + options.output + 'temp/pdfHeader.html');
842
- pdfOptions.push(' --footer-html ' + options.output + 'temp/pdfFooter.html');
843
- pdfOptions.push(' cover ' + options.output + 'temp/pdfCover.html');
844
- pdfOptions.push(
845
- ' toc --xsl-style-sheet ' + __dirname + '/pdf-contents.xsl',
846
- );
847
- let allPages = '';
848
- for (let key in sortedPages) {
849
- if (sortedPages.hasOwnProperty(key)) {
850
- sortedPages[key].forEach((section) => {
851
- section.pages.forEach((page) => {
852
- let key = page.source;
853
- let name = key.substr(0, page.source.lastIndexOf('.'));
854
- let path = options.output + name + '.html';
855
- allPages += ' ' + path;
856
- });
857
- });
858
- }
859
- }
860
- let args = pdfOptions.join('');
861
- args += allPages;
862
- args += ' ' + options.output + pdfName;
863
- return spawnArgs(args);
864
- };
865
-
866
- let checkPdfVersion = () => {
867
- if (options.pdf === true) {
868
- //first check that wkhtmltopdf is installed
869
- exec(options.wkhtmltopdfPath + ' -V', (error, stdout, stderr) => {
870
- if (error) {
871
- console.log(
872
- chalk.red(
873
- 'Unable to call wkhtmltopdf. Is it installed and in path? See http://wkhtmltopdf.org',
874
- ),
875
- );
876
- if (options.verbose === true) {
877
- console.log(chalk.red(error));
878
- }
879
- mainProcess.exit(1);
880
- } else {
881
- //warn if the version of wkhtmltopdf is not an expected version
882
- let actualWkhtmltopdfVersion = stdout.trim();
883
- if (actualWkhtmltopdfVersion !== wkhtmltopdfVersion) {
884
- let warning =
885
- 'Warning: unexpected version of wkhtmltopdf, which may work but is not tested or supported';
886
- let expectedVersion = ' expected version: ' + wkhtmltopdfVersion;
887
- let detectedVersion =
888
- ' detected version: ' + actualWkhtmltopdfVersion;
889
- console.log(chalk.yellow(warning));
890
- console.log(chalk.yellow(expectedVersion));
891
- console.log(chalk.yellow(detectedVersion));
892
- }
893
- generatePdf();
894
- }
895
- });
896
- } else {
897
- cleanUp();
898
- }
899
- };
900
-
901
- /*
902
- call wkhtmltopdf as an external executable
903
- */
904
-
905
- let generatePdf = () => {
906
- console.log(chalk.green('Creating the PDF copy (may take some time)'));
907
- let args = getPdfArguments();
908
- let wkhtmltopdf = spawn(options.wkhtmltopdfPath, args);
909
- let spinner = new cliSpinner(chalk.green(' Processing... %s'));
910
- spinner.setSpinnerString('|/-\\');
911
-
912
- wkhtmltopdf.on('error', (error) => {
913
- console.log(chalk.red('Error calling wkhtmltopdf to generate the PDF'));
914
- if (options.verbose === true) {
915
- console.log(chalk.red(error));
916
- }
917
- });
918
-
919
- if (options.verbose !== true) {
920
- spinner.start(); //only show spinner when verbose is off (otherwise show raw wkhtmltopdf output)
921
- } else {
922
- //pipe the output from wkhtmltopdf back to stdout
923
- //however, wkhtmltpdf outputs to stderr, not stdout. See:
924
- //https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1980
925
- wkhtmltopdf.stderr.pipe(mainProcess.stdout);
926
- }
927
-
928
- wkhtmltopdf.stdout.on('data', (data) => {
929
- //do nothing
930
- });
931
-
932
- wkhtmltopdf.stderr.on('data', (data) => {
933
- //do nothing
934
- });
935
-
936
- wkhtmltopdf.on('close', (code) => {
937
- if (options.verbose !== true) {
938
- spinner.stop();
939
- console.log(''); //newline after spinner stops
940
- }
941
- if (code !== 0) {
942
- let warning =
943
- 'wkhtmltopdf exited with a warning or error: try the -v option for details';
944
- console.log(chalk.yellow(warning));
945
- }
946
- cleanUp();
947
- });
948
- };
949
-
950
- let createRedirect = () => {
951
- if (options.redirect) {
952
- let parent = options.output.replace(/\/$/, ''); //trim any trailing slash
953
- parent = parent.split(path.sep).slice(-1).pop(); //get name of final directory in the path
954
- let homepage = meta.contents[0].pages[0];
955
- homepage =
956
- homepage.source.substr(0, homepage.source.lastIndexOf('.')) + '.html';
957
- let redirectLink = parent + '/' + homepage;
958
- let $ = templates.redirect;
959
- $('a').attr('href', redirectLink);
960
- $('meta[http-equiv=REFRESH]').attr('content', '0;url=' + redirectLink);
961
- let file = options.output + '../' + 'index.html';
962
- try {
963
- fs.outputFileSync(file, $.html(), 'utf-8');
964
- } catch (error) {
965
- console.log(chalk.red('Error writing redirect file: ' + file));
966
- if (options.verbose === true) {
967
- console.log(chalk.red(error));
968
- }
969
- //don't exit because redirect error is not a fatal error
970
- }
971
- }
972
- };
973
-
974
- /*
975
- cleanup
976
- */
977
-
978
- let cleanUp = () => {
979
- createRedirect();
980
- //remove temp files
981
- if (options.pdf === true) {
982
- removeDirSync(options.output + 'temp');
983
- }
984
- console.log(chalk.green.bold('Done!'));
985
- };
986
- }
987
-
988
- module.exports = DocGen;