gtfs-to-html 2.2.0 → 2.3.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.
- package/.eslintrc.json +15 -20
- package/.husky/pre-commit +4 -0
- package/CHANGELOG.md +275 -2
- package/README.md +59 -41
- package/app/index.js +46 -24
- package/bin/gtfs-to-html.js +5 -7
- package/lib/file-utils.js +52 -15
- package/lib/formatters.js +123 -28
- package/lib/geojson-utils.js +32 -17
- package/lib/gtfs-to-html.js +96 -34
- package/lib/log-utils.js +23 -15
- package/lib/template-functions.js +80 -17
- package/lib/time-utils.js +10 -2
- package/lib/utils.js +762 -371
- package/package.json +29 -11
- package/public/css/timetable_styles.css +55 -49
- package/public/js/system-map.js +73 -60
- package/public/js/timetable-map.js +103 -96
- package/public/js/timetable-menu.js +32 -8
- package/views/default/formatting_functions.pug +0 -17
- package/views/default/overview_full.pug +2 -2
- package/views/default/timetablepage_full.pug +2 -2
- package/www/blog/2021-11-06-CSV-Export.md +26 -0
- package/www/docs/configuration.md +87 -85
- package/www/docs/current-usage.md +31 -30
- package/www/docs/introduction.md +8 -5
- package/www/docs/timetables.md +35 -27
- package/www/package.json +2 -5
- package/www/static/img/gtfs-to-html-logo.svg +15 -61
- package/www/yarn.lock +2160 -3398
package/README.md
CHANGED
|
@@ -1,35 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
➡️
|
|
3
|
+
<a href="https://gtfstohtml.com/docs/">Documentation</a> |
|
|
4
|
+
<a href="https://gtfstohtml.com/docs/quick-start">Quick Start</a> |
|
|
5
|
+
<a href="https://gtfstohtml.com/docs/configuration">Configuration</a> |
|
|
6
|
+
<a href="https://gtfstohtml.com/docs/contact">Questions and Support</a>
|
|
7
|
+
⬅️
|
|
8
|
+
<br /><br />
|
|
9
|
+
<img src="www/static/img/gtfs-to-html-logo.svg" alt="GTFS-to-HTML" />
|
|
10
|
+
<br /><br />
|
|
11
|
+
<a href="https://www.npmjs.com/package/gtfs-to-html" rel="nofollow"><img src="https://img.shields.io/npm/v/gtfs-to-html.svg?style=flat" style="max-width: 100%;"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/gtfs-to-html" rel="nofollow"><img src="https://img.shields.io/npm/dm/gtfs-to-html.svg?style=flat" style="max-width: 100%;"></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow.svg">
|
|
14
|
+
<br /><br />
|
|
15
|
+
Create human-readable, user-friendly transit timetables in HTML, PDF or CSV format directly from GTFS.
|
|
16
|
+
<br /><br />
|
|
17
|
+
<a href="https://nodei.co/npm/gtfs-to-html/" rel="nofollow"><img src="https://nodei.co/npm/gtfs-to-html.png?downloads=true" alt="NPM" style="max-width: 100%;"></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<hr>
|
|
21
|
+
|
|
22
|
+
See [gtfstohtml.com](https://gtfstohtml.com) for full documentation.
|
|
23
|
+
|
|
24
|
+
Most transit agencies have schedule data in [GTFS ](https://developers.google.com/transit/gtfs/) format but need to show each route's schedule to users on a website. GTFS-to-HTML automates the process of creating nicely formatted HTML timetables for inclusion on a transit agency website. This makes it easy to keep timetables up to date and accurate when schedule changes happen and reduces the likelihood of errors.
|
|
14
25
|
|
|
15
26
|
<img width="1265" src="https://user-images.githubusercontent.com/96217/28296063-aed45568-6b1a-11e7-9794-94b3d915d668.png">
|
|
16
27
|
|
|
17
28
|
## Features
|
|
18
29
|
|
|
19
30
|
### Configurable and customizable
|
|
20
|
-
|
|
31
|
+
|
|
32
|
+
`gtfs-to-html` has many options that configure how timetables are presented. It also allows using a completely custom template which makes it easy to build chunks of HTML that will fit perfectly into any website using any HTML structure and classes that you'd like. Or, create printable PDF versions or CSV exports of timetables using the `outputFormat` config option.
|
|
21
33
|
|
|
22
34
|
### Accessibility for all
|
|
35
|
+
|
|
23
36
|
`gtfs-to-html` properly formats timetables to ensure they are screen-reader accessible and WCAG 2.0 compliant.
|
|
24
37
|
|
|
25
38
|
### Mobile responsiveness built in
|
|
39
|
+
|
|
26
40
|
Built-in styling makes `gtfs-to-html` timetables ready to size and scroll easily on mobile phones and tablets.
|
|
27
41
|
|
|
28
42
|
### Schedule changes? A cinch.
|
|
43
|
+
|
|
29
44
|
By generating future timetables and including dates in table metadata, your timetables can appear in advance of a schedule change, and you can validate that your new timetables and GTFS are correct.
|
|
30
45
|
|
|
31
46
|
### Auto-generated maps
|
|
32
|
-
|
|
47
|
+
|
|
48
|
+
`gtfs-to-html` can also generate a map for each route that can be included with the schedule page. The map shows all stops for the route and lists all routes that serve each stop. See the `showMap` configuration option below.
|
|
33
49
|
|
|
34
50
|
Note: If you only want maps of GTFS data, use the [gtfs-to-geojson](https://github.com/blinktaginc/gtfs-to-geojson) package instead and skip making timetables entirely. If offers many different formats of GeoJSON for routes and stops.
|
|
35
51
|
|
|
@@ -40,34 +56,36 @@ Note: If you only want maps of GTFS data, use the [gtfs-to-geojson](https://gith
|
|
|
40
56
|
You can now use `gtfs-to-html` without actually downloading any code or doing any configuration. [run.gtfstohtml.com](https://run.gtfstohtml.com) provides a web based interface for finding GTFS feeds for agencies, setting configuration and then generates a previewable and downloadable set of timetables.
|
|
41
57
|
|
|
42
58
|
## Current Usage
|
|
59
|
+
|
|
43
60
|
Many transit agencies use `gtfs-to-html` to generate the schedule pages used on their websites, including:
|
|
44
61
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
- [Advance Transit](https://advancetransit.com)
|
|
63
|
+
- [Brockton Area Transit Authority](https://ridebat.com)
|
|
64
|
+
- [Capital Transit (Helena, Montana)](http://www.ridethecapitalt.org)
|
|
65
|
+
- [Capital Transit (Juneau, Alaska)](https://juneaucapitaltransit.org)
|
|
66
|
+
- [Central Transit (Ellensburg, Washington)](https://centraltransit.org)
|
|
67
|
+
- [County Connection (Contra Costa County, California)](https://countyconnection.com)
|
|
68
|
+
- [El Dorado Transit](http://eldoradotransit.com)
|
|
69
|
+
- [Greater Attleboro-Taunton Regional Transit Authority](https://www.gatra.org)
|
|
70
|
+
- [Humboldt Transit Authority](http://hta.org)
|
|
71
|
+
- [Kings Area Rural Transit (KART)](https://www.kartbus.org)
|
|
72
|
+
- [Madera County Connection](http://mcctransit.com)
|
|
73
|
+
- [Marin Transit](https://marintransit.org)
|
|
74
|
+
- [Morongo Basin Transit Authority](https://mbtabus.com)
|
|
75
|
+
- [Mountain Transit](http://mountaintransit.org)
|
|
76
|
+
- [MVgo (Mountain View, CA)](https://mvgo.org)
|
|
77
|
+
- [NW Connector (Oregon)](http://www.nworegontransit.org)
|
|
78
|
+
- [Palo Verde Valley Transit Agency](http://pvvta.com)
|
|
79
|
+
- [Petaluma Transit](http://transit.cityofpetaluma.net)
|
|
80
|
+
- [RTC Washoe (Reno, NV)](https://www.rtcwashoe.com)
|
|
81
|
+
- [Santa Barbara Metropolitan Transit District](https://sbmtd.gov)
|
|
82
|
+
- [Sonoma County Transit](http://sctransit.com)
|
|
83
|
+
- [Tahoe Transportation District](https://www.tahoetransportation.org)
|
|
84
|
+
- [Tahoe Truckee Area Regional Transit](https://tahoetruckeetransit.com)
|
|
85
|
+
- [Transcollines](https://transcollines.ca)
|
|
86
|
+
- [Tulare County Area Transit](https://ridetcat.org)
|
|
87
|
+
- [Victor Valley Transit](https://vvta.org)
|
|
88
|
+
- [Worcester Regional Transit Authority](https://therta.com)
|
|
71
89
|
|
|
72
90
|
Are you using `gtfs-to-html`? Let us know via email (brendan@blinktag.com) or via opening a github issue or pull request if your agency is using this library.
|
|
73
91
|
|
package/app/index.js
CHANGED
|
@@ -9,20 +9,26 @@ import express from 'express';
|
|
|
9
9
|
import logger from 'morgan';
|
|
10
10
|
|
|
11
11
|
import { formatTimetableLabel } from '../lib/formatters.js';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
import {
|
|
13
|
+
setDefaultConfig,
|
|
14
|
+
getTimetablePagesForAgency,
|
|
15
|
+
getFormattedTimetablePage,
|
|
16
|
+
generateOverviewHTML,
|
|
17
|
+
generateTimetableHTML,
|
|
18
|
+
} from '../lib/utils.js';
|
|
19
|
+
|
|
20
|
+
const { argv } = yargs(process.argv).option('c', {
|
|
21
|
+
alias: 'configPath',
|
|
22
|
+
describe: 'Path to config file',
|
|
23
|
+
default: './config.json',
|
|
24
|
+
type: 'string',
|
|
25
|
+
});
|
|
21
26
|
|
|
22
27
|
const app = express();
|
|
23
28
|
const router = new express.Router();
|
|
24
29
|
|
|
25
|
-
const configPath =
|
|
30
|
+
const configPath =
|
|
31
|
+
argv.configPath || new URL('../config.json', import.meta.url);
|
|
26
32
|
const selectedConfig = JSON.parse(readFileSync(configPath));
|
|
27
33
|
|
|
28
34
|
const config = setDefaultConfig(selectedConfig);
|
|
@@ -33,9 +39,11 @@ config.log = console.log;
|
|
|
33
39
|
config.logWarning = console.warn;
|
|
34
40
|
config.logError = console.error;
|
|
35
41
|
|
|
36
|
-
openDb(config).catch(error => {
|
|
42
|
+
openDb(config).catch((error) => {
|
|
37
43
|
if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
|
|
38
|
-
config.logError(
|
|
44
|
+
config.logError(
|
|
45
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
46
|
+
);
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
throw error;
|
|
@@ -47,14 +55,25 @@ openDb(config).catch(error => {
|
|
|
47
55
|
router.get('/', async (request, response, next) => {
|
|
48
56
|
try {
|
|
49
57
|
const timetablePages = [];
|
|
50
|
-
const timetablePageIds = map(
|
|
58
|
+
const timetablePageIds = map(
|
|
59
|
+
await getTimetablePagesForAgency(config),
|
|
60
|
+
'timetable_page_id'
|
|
61
|
+
);
|
|
51
62
|
|
|
52
63
|
for (const timetablePageId of timetablePageIds) {
|
|
53
64
|
// eslint-disable-next-line no-await-in-loop
|
|
54
|
-
const timetablePage = await getFormattedTimetablePage(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const timetablePage = await getFormattedTimetablePage(
|
|
66
|
+
timetablePageId,
|
|
67
|
+
config
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
!timetablePage.consolidatedTimetables ||
|
|
72
|
+
timetablePage.consolidatedTimetables.length === 0
|
|
73
|
+
) {
|
|
74
|
+
console.error(
|
|
75
|
+
`No timetables found for timetable_page_id=${timetablePage.timetable_page_id}`
|
|
76
|
+
);
|
|
58
77
|
}
|
|
59
78
|
|
|
60
79
|
timetablePage.relativePath = `/timetables/${timetablePage.timetable_page_id}`;
|
|
@@ -76,19 +95,20 @@ router.get('/', async (request, response, next) => {
|
|
|
76
95
|
* Show a specific timetable page
|
|
77
96
|
*/
|
|
78
97
|
router.get('/timetables/:timetablePageId', async (request, response, next) => {
|
|
79
|
-
const {
|
|
80
|
-
timetablePageId
|
|
81
|
-
} = request.params;
|
|
98
|
+
const { timetablePageId } = request.params;
|
|
82
99
|
|
|
83
100
|
if (!timetablePageId) {
|
|
84
101
|
return next(new Error('No timetablePageId provided'));
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
try {
|
|
88
|
-
const timetablePage = await getFormattedTimetablePage(
|
|
105
|
+
const timetablePage = await getFormattedTimetablePage(
|
|
106
|
+
timetablePageId,
|
|
107
|
+
config
|
|
108
|
+
);
|
|
89
109
|
|
|
90
|
-
const
|
|
91
|
-
response.send(
|
|
110
|
+
const html = await generateTimetableHTML(timetablePage, config);
|
|
111
|
+
response.send(html);
|
|
92
112
|
} catch (error) {
|
|
93
113
|
next(error);
|
|
94
114
|
}
|
|
@@ -98,7 +118,9 @@ app.set('views', path.join(fileURLToPath(import.meta.url), '../../views'));
|
|
|
98
118
|
app.set('view engine', 'pug');
|
|
99
119
|
|
|
100
120
|
app.use(logger('dev'));
|
|
101
|
-
app.use(
|
|
121
|
+
app.use(
|
|
122
|
+
express.static(path.join(fileURLToPath(import.meta.url), '../../public'))
|
|
123
|
+
);
|
|
102
124
|
|
|
103
125
|
app.use('/', router);
|
|
104
126
|
app.set('port', process.env.PORT || 3000);
|
package/bin/gtfs-to-html.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import yargs from 'yargs';
|
|
4
|
-
/* eslint-disable-next-line node/file-extension-in-import */
|
|
5
4
|
import { hideBin } from 'yargs/helpers';
|
|
6
5
|
|
|
7
6
|
import { getConfig } from '../lib/file-utils.js';
|
|
@@ -15,22 +14,22 @@ const { argv } = yargs(hideBin(process.argv))
|
|
|
15
14
|
alias: 'configPath',
|
|
16
15
|
describe: 'Path to config file',
|
|
17
16
|
default: './config.json',
|
|
18
|
-
type: 'string'
|
|
17
|
+
type: 'string',
|
|
19
18
|
})
|
|
20
19
|
.option('s', {
|
|
21
20
|
alias: 'skipImport',
|
|
22
21
|
describe: 'Don’t import GTFS file.',
|
|
23
|
-
type: 'boolean'
|
|
22
|
+
type: 'boolean',
|
|
24
23
|
})
|
|
25
24
|
.default('skipImport', undefined)
|
|
26
25
|
.option('t', {
|
|
27
26
|
alias: 'showOnlyTimepoint',
|
|
28
27
|
describe: 'Show only stops with a `timepoint` value in `stops.txt`',
|
|
29
|
-
type: 'boolean'
|
|
28
|
+
type: 'boolean',
|
|
30
29
|
})
|
|
31
30
|
.default('showOnlyTimepoint', undefined);
|
|
32
31
|
|
|
33
|
-
const handleError = error => {
|
|
32
|
+
const handleError = (error) => {
|
|
34
33
|
const text = error || 'Unknown Error';
|
|
35
34
|
process.stdout.write(`\n${formatError(text)}\n`);
|
|
36
35
|
console.error(error);
|
|
@@ -43,5 +42,4 @@ const setupImport = async () => {
|
|
|
43
42
|
process.exit();
|
|
44
43
|
};
|
|
45
44
|
|
|
46
|
-
setupImport()
|
|
47
|
-
.catch(handleError);
|
|
45
|
+
setupImport().catch(handleError);
|
package/lib/file-utils.js
CHANGED
|
@@ -20,8 +20,15 @@ import * as templateFunctions from './template-functions.js';
|
|
|
20
20
|
*/
|
|
21
21
|
export async function getConfig(argv) {
|
|
22
22
|
try {
|
|
23
|
-
const data = await readFile(
|
|
24
|
-
|
|
23
|
+
const data = await readFile(
|
|
24
|
+
path.resolve(untildify(argv.configPath)),
|
|
25
|
+
'utf8'
|
|
26
|
+
).catch((error) => {
|
|
27
|
+
console.error(
|
|
28
|
+
new Error(
|
|
29
|
+
`Cannot find configuration file at \`${argv.configPath}\`. Use config-sample.json as a starting point, pass --configPath option`
|
|
30
|
+
)
|
|
31
|
+
);
|
|
25
32
|
throw error;
|
|
26
33
|
});
|
|
27
34
|
const config = JSON.parse(data);
|
|
@@ -36,7 +43,11 @@ export async function getConfig(argv) {
|
|
|
36
43
|
|
|
37
44
|
return config;
|
|
38
45
|
} catch (error) {
|
|
39
|
-
console.error(
|
|
46
|
+
console.error(
|
|
47
|
+
new Error(
|
|
48
|
+
`Cannot parse configuration file at \`${argv.configPath}\`. Check to ensure that it is valid JSON.`
|
|
49
|
+
)
|
|
50
|
+
);
|
|
40
51
|
throw error;
|
|
41
52
|
}
|
|
42
53
|
}
|
|
@@ -52,15 +63,21 @@ function getTemplatePath(templateFileName, config) {
|
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
if (config.templatePath !== undefined) {
|
|
55
|
-
return path.join(
|
|
66
|
+
return path.join(
|
|
67
|
+
untildify(config.templatePath),
|
|
68
|
+
`${fullTemplateFileName}.pug`
|
|
69
|
+
);
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
return path.join(
|
|
72
|
+
return path.join(
|
|
73
|
+
fileURLToPath(import.meta.url),
|
|
74
|
+
'../../views/default',
|
|
75
|
+
`${fullTemplateFileName}.pug`
|
|
76
|
+
);
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
/*
|
|
62
|
-
* Prepare the specified directory for saving HTML timetables by deleting
|
|
63
|
-
* everything and creating the expected folders.
|
|
80
|
+
* Prepare the specified directory for saving HTML timetables by deleting everything.
|
|
64
81
|
*/
|
|
65
82
|
export async function prepDirectory(exportPath) {
|
|
66
83
|
await rm(exportPath, { recursive: true, force: true });
|
|
@@ -68,7 +85,9 @@ export async function prepDirectory(exportPath) {
|
|
|
68
85
|
await mkdir(exportPath, { recursive: true });
|
|
69
86
|
} catch (error) {
|
|
70
87
|
if (error.code === 'ENOENT') {
|
|
71
|
-
throw new Error(
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Unable to write to ${exportPath}. Try running this command from a writable directory.`
|
|
90
|
+
);
|
|
72
91
|
}
|
|
73
92
|
|
|
74
93
|
throw error;
|
|
@@ -79,7 +98,10 @@ export async function prepDirectory(exportPath) {
|
|
|
79
98
|
* Copy needed CSS and JS to export path.
|
|
80
99
|
*/
|
|
81
100
|
export function copyStaticAssets(exportPath) {
|
|
82
|
-
const staticAssetPath = path.join(
|
|
101
|
+
const staticAssetPath = path.join(
|
|
102
|
+
fileURLToPath(import.meta.url),
|
|
103
|
+
'../../public'
|
|
104
|
+
);
|
|
83
105
|
copydir.sync(path.join(staticAssetPath, 'css'), path.join(exportPath, 'css'));
|
|
84
106
|
copydir.sync(path.join(staticAssetPath, 'js'), path.join(exportPath, 'js'));
|
|
85
107
|
}
|
|
@@ -96,7 +118,7 @@ export function zipFolder(exportPath) {
|
|
|
96
118
|
archive.on('error', reject);
|
|
97
119
|
archive.pipe(output);
|
|
98
120
|
archive.glob('**/*.{txt,css,js,html}', {
|
|
99
|
-
cwd: exportPath
|
|
121
|
+
cwd: exportPath,
|
|
100
122
|
});
|
|
101
123
|
archive.finalize();
|
|
102
124
|
});
|
|
@@ -109,7 +131,9 @@ export function generateFileName(timetable, config) {
|
|
|
109
131
|
let filename = timetable.timetable_id;
|
|
110
132
|
|
|
111
133
|
for (const route of timetable.routes) {
|
|
112
|
-
filename += isNullOrEmpty(route.route_short_name)
|
|
134
|
+
filename += isNullOrEmpty(route.route_short_name)
|
|
135
|
+
? `_${route.route_long_name.replace(/\s/g, '-')}`
|
|
136
|
+
: `_${route.route_short_name.replace(/\s/g, '-')}`;
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
@@ -121,6 +145,19 @@ export function generateFileName(timetable, config) {
|
|
|
121
145
|
return sanitize(filename).toLowerCase();
|
|
122
146
|
}
|
|
123
147
|
|
|
148
|
+
/*
|
|
149
|
+
* Generate the filename for a CSV timetable.
|
|
150
|
+
*/
|
|
151
|
+
export function generateCSVFileName(timetable, timetablePage) {
|
|
152
|
+
let filename = timetablePage.filename.replace(/.html$/, '');
|
|
153
|
+
|
|
154
|
+
if (timetablePage.timetables.length > 1) {
|
|
155
|
+
filename += `_${timetable.direction_id}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return sanitize(`${filename}.csv`);
|
|
159
|
+
}
|
|
160
|
+
|
|
124
161
|
/*
|
|
125
162
|
* Generates the folder name for a timetable page based on the date.
|
|
126
163
|
*/
|
|
@@ -144,13 +181,13 @@ export async function renderTemplate(templateFileName, templateVars, config) {
|
|
|
144
181
|
const html = await renderFile(templatePath, {
|
|
145
182
|
_,
|
|
146
183
|
...templateFunctions,
|
|
147
|
-
...templateVars
|
|
184
|
+
...templateVars,
|
|
148
185
|
});
|
|
149
186
|
|
|
150
187
|
// Beautify HTML if `beautify` is set in config.
|
|
151
188
|
if (config.beautify === true) {
|
|
152
189
|
return beautify.html_beautify(html, {
|
|
153
|
-
indent_size: 2
|
|
190
|
+
indent_size: 2,
|
|
154
191
|
});
|
|
155
192
|
}
|
|
156
193
|
|
|
@@ -166,10 +203,10 @@ export async function renderPdf(htmlPath) {
|
|
|
166
203
|
const page = await browser.newPage();
|
|
167
204
|
await page.emulateMediaType('screen');
|
|
168
205
|
await page.goto(`file://${htmlPath}`, {
|
|
169
|
-
waitUntil: 'networkidle0'
|
|
206
|
+
waitUntil: 'networkidle0',
|
|
170
207
|
});
|
|
171
208
|
await page.pdf({
|
|
172
|
-
path: pdfPath
|
|
209
|
+
path: pdfPath,
|
|
173
210
|
});
|
|
174
211
|
|
|
175
212
|
await browser.close();
|