gtfs-to-html 2.9.2 → 2.9.4
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/dist/app/index.js +30 -19
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +92 -64
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +91 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/views/default/css/timetable_styles.css +155 -1
- package/views/default/formatting_functions.pug +13 -12
- package/views/default/js/timetable-alerts.js +180 -0
- package/views/default/js/timetable-map.js +10 -5
- package/views/default/timetablepage.pug +12 -4
- package/views/default/timetablepage_full.pug +7 -6
- package/views/default/css/timetable_pdf_styles.css +0 -69
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ interface IConfig {
|
|
|
7
7
|
exclude?: string[];
|
|
8
8
|
}[];
|
|
9
9
|
coordinatePrecision?: number;
|
|
10
|
-
outputType?: string;
|
|
11
10
|
outputFormat?: string;
|
|
11
|
+
outputPath?: string;
|
|
12
12
|
startDate?: string;
|
|
13
13
|
endDate?: string;
|
|
14
14
|
verbose?: boolean;
|
|
@@ -19,6 +19,6 @@ interface IConfig {
|
|
|
19
19
|
logError: (text: string) => void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
declare const gtfsToHtml: (initialConfig: IConfig) => Promise<
|
|
22
|
+
declare const gtfsToHtml: (initialConfig: IConfig) => Promise<string>;
|
|
23
23
|
|
|
24
24
|
export { gtfsToHtml as default };
|
package/dist/index.js
CHANGED
|
@@ -5,18 +5,26 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/lib/gtfs-to-html.ts
|
|
8
|
-
import
|
|
8
|
+
import path from "node:path";
|
|
9
9
|
import { mkdir as mkdir2, writeFile } from "node:fs/promises";
|
|
10
10
|
import { map } from "lodash-es";
|
|
11
11
|
import { openDb as openDb2, importGtfs } from "gtfs";
|
|
12
12
|
import sanitize2 from "sanitize-filename";
|
|
13
13
|
import Timer from "timer-machine";
|
|
14
|
+
import untildify2 from "untildify";
|
|
14
15
|
|
|
15
16
|
// src/lib/file-utils.ts
|
|
16
|
-
import
|
|
17
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
18
|
import { createWriteStream } from "node:fs";
|
|
18
19
|
import { fileURLToPath } from "node:url";
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
access,
|
|
22
|
+
cp,
|
|
23
|
+
copyFile,
|
|
24
|
+
mkdir,
|
|
25
|
+
readdir,
|
|
26
|
+
readFile
|
|
27
|
+
} from "node:fs/promises";
|
|
20
28
|
import _ from "lodash-es";
|
|
21
29
|
import archiver from "archiver";
|
|
22
30
|
import beautify from "js-beautify";
|
|
@@ -325,7 +333,7 @@ function formatTripNameForCSV(trip, timetable) {
|
|
|
325
333
|
}
|
|
326
334
|
|
|
327
335
|
// package.json
|
|
328
|
-
var version = "2.9.
|
|
336
|
+
var version = "2.9.3";
|
|
329
337
|
|
|
330
338
|
// src/lib/utils.ts
|
|
331
339
|
var isTimepoint = (stoptime) => {
|
|
@@ -1304,7 +1312,10 @@ function setDefaultConfig(initialConfig) {
|
|
|
1304
1312
|
config.menuType = "none";
|
|
1305
1313
|
}
|
|
1306
1314
|
config.hasGtfsRealtime = config.agencies.some(
|
|
1307
|
-
(agency) => agency.realtimeTripUpdates?.url || agency.realtimeVehiclePositions?.url
|
|
1315
|
+
(agency) => agency.realtimeTripUpdates?.url || agency.realtimeVehiclePositions?.url || agency.realtimeAlerts?.url
|
|
1316
|
+
);
|
|
1317
|
+
config.hasGtfsRealtimeAlerts = config.agencies.some(
|
|
1318
|
+
(agency) => agency.realtimeAlerts?.url
|
|
1308
1319
|
);
|
|
1309
1320
|
return config;
|
|
1310
1321
|
}
|
|
@@ -1787,61 +1798,77 @@ function mergeTimetablesWithSameId(timetables) {
|
|
|
1787
1798
|
}
|
|
1788
1799
|
|
|
1789
1800
|
// src/lib/file-utils.ts
|
|
1790
|
-
function
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1801
|
+
function getPathToViewsFolder(config) {
|
|
1802
|
+
if (config.templatePath) {
|
|
1803
|
+
return untildify(config.templatePath);
|
|
1804
|
+
}
|
|
1805
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1806
|
+
let viewsFolderPath;
|
|
1807
|
+
if (__dirname.endsWith("/dist/bin") || __dirname.endsWith("/dist/app")) {
|
|
1808
|
+
viewsFolderPath = resolve(__dirname, "../../views/default");
|
|
1809
|
+
} else if (__dirname.endsWith("/dist")) {
|
|
1810
|
+
viewsFolderPath = resolve(__dirname, "../views/default");
|
|
1811
|
+
} else {
|
|
1812
|
+
viewsFolderPath = resolve(__dirname, "views/default");
|
|
1794
1813
|
}
|
|
1795
|
-
|
|
1796
|
-
return path.join(templatePath, `${fullTemplateFileName}.pug`);
|
|
1814
|
+
return viewsFolderPath;
|
|
1797
1815
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1816
|
+
function getPathToTemplateFile(templateFileName, config) {
|
|
1817
|
+
const fullTemplateFileName = config.noHead !== true ? `${templateFileName}_full.pug` : `${templateFileName}.pug`;
|
|
1818
|
+
return join(getPathToViewsFolder(config), fullTemplateFileName);
|
|
1819
|
+
}
|
|
1820
|
+
async function prepDirectory(outputPath) {
|
|
1800
1821
|
try {
|
|
1801
|
-
await
|
|
1822
|
+
await access(outputPath);
|
|
1802
1823
|
} catch (error) {
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
)
|
|
1824
|
+
try {
|
|
1825
|
+
await mkdir(outputPath, { recursive: true });
|
|
1826
|
+
} catch (error2) {
|
|
1827
|
+
if (error2?.code === "ENOENT") {
|
|
1828
|
+
throw new Error(
|
|
1829
|
+
`Unable to write to ${outputPath}. Try running this command from a writable directory.`
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
throw error2;
|
|
1807
1833
|
}
|
|
1808
|
-
|
|
1834
|
+
}
|
|
1835
|
+
const files = await readdir(outputPath);
|
|
1836
|
+
if (files.length > 0) {
|
|
1837
|
+
throw new Error(
|
|
1838
|
+
`Output directory ${outputPath} is not empty. Please specify an empty directory.`
|
|
1839
|
+
);
|
|
1809
1840
|
}
|
|
1810
1841
|
}
|
|
1811
|
-
async function copyStaticAssets(config,
|
|
1812
|
-
const
|
|
1842
|
+
async function copyStaticAssets(config, outputPath) {
|
|
1843
|
+
const viewsFolderPath = getPathToViewsFolder(config);
|
|
1813
1844
|
const foldersToCopy = ["css", "js", "img"];
|
|
1814
1845
|
for (const folder of foldersToCopy) {
|
|
1815
|
-
if (await access(
|
|
1816
|
-
await cp(
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
{
|
|
1820
|
-
recursive: true
|
|
1821
|
-
}
|
|
1822
|
-
);
|
|
1846
|
+
if (await access(join(viewsFolderPath, folder)).then(() => true).catch(() => false)) {
|
|
1847
|
+
await cp(join(viewsFolderPath, folder), join(outputPath, folder), {
|
|
1848
|
+
recursive: true
|
|
1849
|
+
});
|
|
1823
1850
|
}
|
|
1824
1851
|
}
|
|
1825
1852
|
if (config.hasGtfsRealtime) {
|
|
1826
1853
|
await copyFile(
|
|
1827
1854
|
"node_modules/pbf/dist/pbf.js",
|
|
1828
|
-
|
|
1855
|
+
join(outputPath, "js/pbf.js")
|
|
1829
1856
|
);
|
|
1830
1857
|
await copyFile(
|
|
1831
1858
|
"node_modules/gtfs-realtime-pbf-js-module/gtfs-realtime.browser.proto.js",
|
|
1832
|
-
|
|
1859
|
+
join(outputPath, "js/gtfs-realtime.browser.proto.js")
|
|
1833
1860
|
);
|
|
1834
1861
|
}
|
|
1835
1862
|
}
|
|
1836
|
-
function zipFolder(
|
|
1837
|
-
const output = createWriteStream(
|
|
1863
|
+
function zipFolder(outputPath) {
|
|
1864
|
+
const output = createWriteStream(join(outputPath, "timetables.zip"));
|
|
1838
1865
|
const archive = archiver("zip");
|
|
1839
|
-
return new Promise((
|
|
1840
|
-
output.on("close",
|
|
1866
|
+
return new Promise((resolve2, reject) => {
|
|
1867
|
+
output.on("close", resolve2);
|
|
1841
1868
|
archive.on("error", reject);
|
|
1842
1869
|
archive.pipe(output);
|
|
1843
1870
|
archive.glob("**/*.{txt,css,js,html}", {
|
|
1844
|
-
cwd:
|
|
1871
|
+
cwd: outputPath
|
|
1845
1872
|
});
|
|
1846
1873
|
archive.finalize();
|
|
1847
1874
|
});
|
|
@@ -1865,7 +1892,7 @@ function generateFolderName(timetablePage) {
|
|
|
1865
1892
|
return sanitize(`${timetable.start_date}-${timetable.end_date}`);
|
|
1866
1893
|
}
|
|
1867
1894
|
async function renderTemplate(templateFileName, templateVars, config) {
|
|
1868
|
-
const templatePath =
|
|
1895
|
+
const templatePath = getPathToTemplateFile(templateFileName, config);
|
|
1869
1896
|
const html = await renderFile(templatePath, {
|
|
1870
1897
|
_,
|
|
1871
1898
|
md: (text) => insane(marked.parseInline(text)),
|
|
@@ -2075,8 +2102,8 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2075
2102
|
const agencyKey = config.agencies.map(
|
|
2076
2103
|
(agency) => agency.agencyKey ?? agency.agency_key ?? "unknown"
|
|
2077
2104
|
).join("-");
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2105
|
+
const outputPath = config.outputPath ? untildify2(config.outputPath) : path.join(process.cwd(), "html", sanitize2(agencyKey));
|
|
2106
|
+
const stats = {
|
|
2080
2107
|
timetables: 0,
|
|
2081
2108
|
timetablePages: 0,
|
|
2082
2109
|
calendars: 0,
|
|
@@ -2090,9 +2117,9 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2090
2117
|
getTimetablePagesForAgency(config),
|
|
2091
2118
|
"timetable_page_id"
|
|
2092
2119
|
);
|
|
2093
|
-
await prepDirectory(
|
|
2120
|
+
await prepDirectory(outputPath);
|
|
2094
2121
|
if (config.noHead !== true && ["html", "pdf"].includes(config.outputFormat)) {
|
|
2095
|
-
await copyStaticAssets(config,
|
|
2122
|
+
await copyStaticAssets(config, outputPath);
|
|
2096
2123
|
}
|
|
2097
2124
|
const bar = progressBar(
|
|
2098
2125
|
`${agencyKey}: Generating ${config.outputFormat.toUpperCase()} timetables {bar} {value}/{total}`,
|
|
@@ -2107,7 +2134,7 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2107
2134
|
);
|
|
2108
2135
|
for (const timetable of timetablePage.timetables) {
|
|
2109
2136
|
for (const warning of timetable.warnings) {
|
|
2110
|
-
|
|
2137
|
+
stats.warnings.push(warning);
|
|
2111
2138
|
bar?.interrupt(warning);
|
|
2112
2139
|
}
|
|
2113
2140
|
}
|
|
@@ -2116,20 +2143,20 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2116
2143
|
`No timetables found for timetable_page_id=${timetablePage.timetable_page_id}`
|
|
2117
2144
|
);
|
|
2118
2145
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2146
|
+
stats.timetables += timetablePage.consolidatedTimetables.length;
|
|
2147
|
+
stats.timetablePages += 1;
|
|
2121
2148
|
const datePath = generateFolderName(timetablePage);
|
|
2122
|
-
await mkdir2(
|
|
2149
|
+
await mkdir2(path.join(outputPath, datePath), { recursive: true });
|
|
2123
2150
|
config.assetPath = "../";
|
|
2124
|
-
timetablePage.relativePath =
|
|
2151
|
+
timetablePage.relativePath = path.join(
|
|
2125
2152
|
datePath,
|
|
2126
2153
|
sanitize2(timetablePage.filename)
|
|
2127
2154
|
);
|
|
2128
2155
|
if (config.outputFormat === "csv") {
|
|
2129
2156
|
for (const timetable of timetablePage.consolidatedTimetables) {
|
|
2130
2157
|
const csv = await generateTimetableCSV(timetable);
|
|
2131
|
-
const csvPath =
|
|
2132
|
-
|
|
2158
|
+
const csvPath = path.join(
|
|
2159
|
+
outputPath,
|
|
2133
2160
|
datePath,
|
|
2134
2161
|
generateFileName(timetable, config, "csv")
|
|
2135
2162
|
);
|
|
@@ -2137,8 +2164,8 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2137
2164
|
}
|
|
2138
2165
|
} else {
|
|
2139
2166
|
const html = await generateTimetableHTML(timetablePage, config);
|
|
2140
|
-
const htmlPath =
|
|
2141
|
-
|
|
2167
|
+
const htmlPath = path.join(
|
|
2168
|
+
outputPath,
|
|
2142
2169
|
datePath,
|
|
2143
2170
|
sanitize2(timetablePage.filename)
|
|
2144
2171
|
);
|
|
@@ -2149,12 +2176,12 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2149
2176
|
}
|
|
2150
2177
|
timetablePages.push(timetablePage);
|
|
2151
2178
|
const timetableStats = generateStats(timetablePage);
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2179
|
+
stats.stops += timetableStats.stops;
|
|
2180
|
+
stats.routes += timetableStats.routes;
|
|
2181
|
+
stats.trips += timetableStats.trips;
|
|
2182
|
+
stats.calendars += timetableStats.calendars;
|
|
2156
2183
|
} catch (error) {
|
|
2157
|
-
|
|
2184
|
+
stats.warnings.push(error?.message);
|
|
2158
2185
|
bar?.interrupt(error.message);
|
|
2159
2186
|
}
|
|
2160
2187
|
bar?.increment();
|
|
@@ -2162,26 +2189,27 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2162
2189
|
if (config.outputFormat === "html") {
|
|
2163
2190
|
config.assetPath = "";
|
|
2164
2191
|
const html = await generateOverviewHTML(timetablePages, config);
|
|
2165
|
-
await writeFile(
|
|
2192
|
+
await writeFile(path.join(outputPath, "index.html"), html);
|
|
2166
2193
|
}
|
|
2167
|
-
const logText = generateLogText(
|
|
2168
|
-
await writeFile(
|
|
2194
|
+
const logText = generateLogText(stats, config);
|
|
2195
|
+
await writeFile(path.join(outputPath, "log.txt"), logText);
|
|
2169
2196
|
if (config.zipOutput) {
|
|
2170
|
-
await zipFolder(
|
|
2197
|
+
await zipFolder(outputPath);
|
|
2171
2198
|
}
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2199
|
+
const fullOutputPath = path.join(
|
|
2200
|
+
outputPath,
|
|
2174
2201
|
config.zipOutput ? "/timetables.zip" : ""
|
|
2175
2202
|
);
|
|
2176
2203
|
config.log(
|
|
2177
|
-
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${
|
|
2204
|
+
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullOutputPath}`
|
|
2178
2205
|
);
|
|
2179
|
-
logStats(
|
|
2206
|
+
logStats(stats, config);
|
|
2180
2207
|
const seconds = Math.round(timer.time() / 1e3);
|
|
2181
2208
|
config.log(
|
|
2182
2209
|
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${seconds} seconds`
|
|
2183
2210
|
);
|
|
2184
2211
|
timer.stop();
|
|
2212
|
+
return fullOutputPath;
|
|
2185
2213
|
};
|
|
2186
2214
|
var gtfs_to_html_default = gtfsToHtml;
|
|
2187
2215
|
export {
|