gtfs-to-html 2.9.13 → 2.9.15

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/README.md CHANGED
@@ -59,8 +59,10 @@ You can now use `gtfs-to-html` without actually downloading any code or doing an
59
59
 
60
60
  Many transit agencies use `gtfs-to-html` to generate the schedule pages used on their websites, including:
61
61
 
62
- - [Advance Transit](https://advancetransit.com)
62
+ - [Advance Transit (Vermont)](https://advancetransit.com)
63
+ - [Basin Transit (Morongo Basin, California)](https://basin-transit.com/)
63
64
  - [Brockton Area Transit Authority](https://ridebat.com)
65
+ - [BusWay – CIRA (Aveiro, Portugal)](https://busway-cira.pt)
64
66
  - [Capital Transit (Helena, Montana)](http://www.ridethecapitalt.org)
65
67
  - [Capital Transit (Juneau, Alaska)](https://juneaucapitaltransit.org)
66
68
  - [Central Transit (Ellensburg, Washington)](https://centraltransit.org)
@@ -69,6 +71,7 @@ Many transit agencies use `gtfs-to-html` to generate the schedule pages used on
69
71
  - [Greater Attleboro-Taunton Regional Transit Authority](https://www.gatra.org)
70
72
  - [Humboldt Transit Authority](http://hta.org)
71
73
  - [Kings Area Rural Transit (KART)](https://www.kartbus.org)
74
+ - [Lowell Regional Transit Authority](https://lrta.com)
72
75
  - [Madera County Connection](http://mcctransit.com)
73
76
  - [Marin Transit](https://marintransit.org)
74
77
  - [Morongo Basin Transit Authority](https://mbtabus.com)
package/dist/app/index.js CHANGED
@@ -13,7 +13,6 @@ import yargs from "yargs";
13
13
  import { hideBin } from "yargs/helpers";
14
14
  import { openDb as openDb2 } from "gtfs";
15
15
  import express from "express";
16
- import logger from "morgan";
17
16
  import untildify2 from "untildify";
18
17
 
19
18
  // src/lib/formatters.ts
@@ -133,7 +132,7 @@ import toposort from "toposort";
133
132
  // src/lib/file-utils.ts
134
133
  import { dirname, join, resolve } from "node:path";
135
134
  import { fileURLToPath } from "node:url";
136
- import _ from "lodash-es";
135
+ import * as _ from "lodash-es";
137
136
  import archiver from "archiver";
138
137
  import beautify from "js-beautify";
139
138
  import { renderFile } from "pug";
@@ -376,7 +375,7 @@ function getAgencyGeoJSON(config2) {
376
375
  }
377
376
 
378
377
  // package.json
379
- var version = "2.9.13";
378
+ var version = "2.9.15";
380
379
 
381
380
  // src/lib/utils.ts
382
381
  var isTimepoint = (stoptime) => {
@@ -430,12 +429,12 @@ var deduplicateTrips = (trips, commonStopId) => {
430
429
  }) : trip.stoptimes[0];
431
430
  const similarTrips = deduplicatedTrips.filter((trip2) => {
432
431
  const stoptime = find(trip2.stoptimes, {
433
- stop_id: selectedStoptime.stop_id
432
+ stop_id: selectedStoptime?.stop_id
434
433
  });
435
434
  if (!stoptime) {
436
435
  return false;
437
436
  }
438
- return stoptime.departure_time === selectedStoptime.departure_time;
437
+ return stoptime.departure_time === selectedStoptime?.departure_time;
439
438
  });
440
439
  const tripIsUnique = every2(similarTrips, (similarTrip) => {
441
440
  const similarTripStoptimes = similarTrip.stoptimes.map(
@@ -467,8 +466,10 @@ var sortTrips = (trips, config2) => {
467
466
  if (trip.stoptimes.length === 0) {
468
467
  continue;
469
468
  }
470
- trip.firstStoptime = timeToSeconds(first(trip.stoptimes).departure_time);
471
- trip.lastStoptime = timeToSeconds(last(trip.stoptimes).departure_time);
469
+ trip.firstStoptime = timeToSeconds(trip.stoptimes[0].departure_time);
470
+ trip.lastStoptime = timeToSeconds(
471
+ trip.stoptimes[trip.stoptimes.length - 1].departure_time
472
+ );
472
473
  }
473
474
  sortedTrips = sortBy(
474
475
  trips,
@@ -480,8 +481,10 @@ var sortTrips = (trips, config2) => {
480
481
  if (trip.stoptimes.length === 0) {
481
482
  continue;
482
483
  }
483
- trip.firstStoptime = timeToSeconds(first(trip.stoptimes).departure_time);
484
- trip.lastStoptime = timeToSeconds(last(trip.stoptimes).departure_time);
484
+ trip.firstStoptime = timeToSeconds(trip.stoptimes[0].departure_time);
485
+ trip.lastStoptime = timeToSeconds(
486
+ trip.stoptimes[trip.stoptimes.length - 1].departure_time
487
+ );
485
488
  }
486
489
  sortedTrips = sortBy(
487
490
  trips,
@@ -547,8 +550,8 @@ var getDaysFromCalendars = (calendars) => {
547
550
  sunday: 0
548
551
  };
549
552
  for (const calendar of calendars) {
550
- for (const [day, value] of Object.entries(days2)) {
551
- days2[day] = value | calendar[day];
553
+ for (const day of Object.keys(days2)) {
554
+ days2[day] = days2[day] | calendar[day];
552
555
  }
553
556
  }
554
557
  return days2;
@@ -823,20 +826,27 @@ var getStopOrder = (timetable, config2) => {
823
826
  stopGraph.push([stopId, sortedStopIds[index + 1]]);
824
827
  }
825
828
  }
826
- const stopIds2 = toposort(stopGraph);
829
+ const stopIds = toposort(stopGraph);
827
830
  return duplicateStopsForDifferentArrivalDeparture(
828
- stopIds2,
831
+ stopIds,
829
832
  timetable,
830
833
  config2
831
834
  );
832
835
  } catch {
836
+ const longestTripStoptimes = getLongestTripStoptimes(
837
+ timetable.orderedTrips,
838
+ config2
839
+ );
840
+ const stopIds = longestTripStoptimes.map((stoptime) => stoptime.stop_id);
841
+ config2.logWarning(
842
+ `Timetable ${timetable.timetable_id} stops are unable to be topologically sorted and has no \`timetable_stop_order.txt\`. Falling back to using the using the stop order from trip with most stoptimes, but this can result in timetables with some stops missing. Try manually specifying stops with \`timetable_stop_order.txt\`.`
843
+ );
844
+ return duplicateStopsForDifferentArrivalDeparture(
845
+ stopIds,
846
+ timetable,
847
+ config2
848
+ );
833
849
  }
834
- const longestTripStoptimes = getLongestTripStoptimes(
835
- timetable.orderedTrips,
836
- config2
837
- );
838
- const stopIds = longestTripStoptimes.map((stoptime) => stoptime.stop_id);
839
- return duplicateStopsForDifferentArrivalDeparture(stopIds, timetable, config2);
840
850
  };
841
851
  var getStopsForTimetable = (timetable, config2) => {
842
852
  if (timetable.orderedTrips.length === 0) {
@@ -1328,6 +1338,7 @@ function setDefaultConfig(initialConfig) {
1328
1338
  noServiceSymbol: "-",
1329
1339
  noServiceText: "No service at this stop",
1330
1340
  outputFormat: "html",
1341
+ overwriteExistingFiles: true,
1331
1342
  requestDropoffSymbol: "\u2020",
1332
1343
  requestDropoffText: "Must request drop off",
1333
1344
  requestPickupSymbol: "***",
@@ -1814,7 +1825,6 @@ var argv = yargs(hideBin(process.argv)).option("c", {
1814
1825
  type: "string"
1815
1826
  }).parseSync();
1816
1827
  var app = express();
1817
- var router = express.Router();
1818
1828
  var configPath = argv.configPath || join2(process.cwd(), "config.json");
1819
1829
  var selectedConfig = JSON.parse(readFileSync(configPath, "utf8"));
1820
1830
  var config = setDefaultConfig(selectedConfig);
@@ -1833,7 +1843,36 @@ try {
1833
1843
  }
1834
1844
  throw error;
1835
1845
  }
1836
- router.get("/", async (request, response, next) => {
1846
+ app.set("views", getPathToViewsFolder(config));
1847
+ app.set("view engine", "pug");
1848
+ app.use((req, res, next) => {
1849
+ console.log(`${req.method} ${req.url}`);
1850
+ next();
1851
+ });
1852
+ var staticAssetPath = config.templatePath === void 0 ? getPathToViewsFolder(config) : untildify2(config.templatePath);
1853
+ app.use(express.static(staticAssetPath));
1854
+ app.use(
1855
+ "/js",
1856
+ express.static(
1857
+ join2(dirname2(fileURLToPath2(import.meta.resolve("pbf"))), "dist")
1858
+ )
1859
+ );
1860
+ app.use(
1861
+ "/js",
1862
+ express.static(
1863
+ dirname2(fileURLToPath2(import.meta.resolve("gtfs-realtime-pbf-js-module")))
1864
+ )
1865
+ );
1866
+ app.use(
1867
+ "/js",
1868
+ express.static(
1869
+ join2(
1870
+ dirname2(fileURLToPath2(import.meta.resolve("anchorme"))),
1871
+ "../../dist/browser"
1872
+ )
1873
+ )
1874
+ );
1875
+ app.get("/", async (req, res, next) => {
1837
1876
  try {
1838
1877
  const timetablePages = [];
1839
1878
  const timetablePageIds = map(
@@ -1849,6 +1888,7 @@ router.get("/", async (request, response, next) => {
1849
1888
  console.error(
1850
1889
  `No timetables found for timetable_page_id=${timetablePage.timetable_page_id}`
1851
1890
  );
1891
+ continue;
1852
1892
  }
1853
1893
  timetablePage.relativePath = `/timetables/${timetablePage.timetable_page_id}`;
1854
1894
  for (const timetable of timetablePage.consolidatedTimetables) {
@@ -1857,57 +1897,66 @@ router.get("/", async (request, response, next) => {
1857
1897
  timetablePages.push(timetablePage);
1858
1898
  }
1859
1899
  const html = await generateOverviewHTML(timetablePages, config);
1860
- response.send(html);
1900
+ res.send(html);
1861
1901
  } catch (error) {
1862
- console.error(error);
1863
1902
  next(error);
1864
1903
  }
1865
1904
  });
1866
- router.get("/timetables/:timetablePageId", async (request, response, next) => {
1867
- const { timetablePageId } = request.params;
1905
+ app.get("/timetables/:timetablePageId", async (req, res, next) => {
1906
+ const { timetablePageId } = req.params;
1868
1907
  if (!timetablePageId) {
1869
- return next(new Error("No timetablePageId provided"));
1908
+ res.status(400).send("No timetablePageId provided");
1909
+ return;
1870
1910
  }
1871
1911
  try {
1872
1912
  const timetablePage = await getFormattedTimetablePage(
1873
1913
  timetablePageId,
1874
1914
  config
1875
1915
  );
1916
+ if (!timetablePage || !timetablePage.consolidatedTimetables || timetablePage.consolidatedTimetables.length === 0) {
1917
+ res.status(404).send("Timetable page not found");
1918
+ return;
1919
+ }
1876
1920
  const html = await generateTimetableHTML(timetablePage, config);
1877
- response.send(html);
1921
+ res.send(html);
1878
1922
  } catch (error) {
1923
+ if (error?.message.startsWith("No timetable found")) {
1924
+ res.status(404).send("Timetable page not found");
1925
+ return;
1926
+ }
1879
1927
  next(error);
1880
1928
  }
1881
1929
  });
1882
- app.set("views", getPathToViewsFolder(config));
1883
- app.set("view engine", "pug");
1884
- app.use(logger("dev"));
1885
- var staticAssetPath = config.templatePath === void 0 ? getPathToViewsFolder(config) : untildify2(config.templatePath);
1886
- app.use(express.static(staticAssetPath));
1887
- app.use(
1888
- "/js",
1889
- express.static(
1890
- join2(dirname2(fileURLToPath2(import.meta.resolve("pbf"))), "dist")
1891
- )
1892
- );
1893
- app.use(
1894
- "/js",
1895
- express.static(
1896
- dirname2(fileURLToPath2(import.meta.resolve("gtfs-realtime-pbf-js-module")))
1897
- )
1898
- );
1930
+ app.use((req, res) => {
1931
+ res.status(404).send("Not Found");
1932
+ });
1899
1933
  app.use(
1900
- "/js",
1901
- express.static(
1902
- join2(
1903
- dirname2(fileURLToPath2(import.meta.resolve("anchorme"))),
1904
- "../../dist/browser"
1905
- )
1906
- )
1934
+ (err, req, res, next) => {
1935
+ console.error(err.stack);
1936
+ res.status(500).send("Something broke!");
1937
+ }
1907
1938
  );
1908
- app.use("/", router);
1909
- app.set("port", process.env.PORT || 3e3);
1910
- var server = app.listen(app.get("port"), () => {
1911
- console.log(`Express server listening on port ${app.get("port")}`);
1912
- });
1939
+ var startServer = async (port2) => {
1940
+ try {
1941
+ await new Promise((resolve2, reject) => {
1942
+ const server = app.listen(port2).once("listening", () => {
1943
+ console.log(`Express server listening on port ${port2}`);
1944
+ resolve2();
1945
+ }).once("error", (err) => {
1946
+ if (err.code === "EADDRINUSE") {
1947
+ console.log(`Port ${port2} is in use, trying ${port2 + 1}`);
1948
+ server.close();
1949
+ resolve2(startServer(port2 + 1));
1950
+ } else {
1951
+ reject(err);
1952
+ }
1953
+ });
1954
+ });
1955
+ } catch (err) {
1956
+ console.error("Failed to start server:", err);
1957
+ process.exit(1);
1958
+ }
1959
+ };
1960
+ var port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3e3;
1961
+ startServer(port);
1913
1962
  //# sourceMappingURL=index.js.map