gtfs-to-html 2.12.1 → 2.12.3

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.
@@ -25,7 +25,6 @@ import {
25
25
  rm
26
26
  } from "fs/promises";
27
27
  import { homedir } from "os";
28
- import { findPackageJSON } from "module";
29
28
  import * as _ from "lodash-es";
30
29
  import { uniqBy as uniqBy2 } from "lodash-es";
31
30
  import archiver from "archiver";
@@ -62,11 +61,11 @@ function fromGTFSTime(timeString) {
62
61
  function toGTFSTime(time) {
63
62
  return time.format("HH:mm:ss");
64
63
  }
65
- function calendarToCalendarCode(c) {
66
- if (Object.values(c).every((value) => value === null)) {
64
+ function calendarToCalendarCode(calendar) {
65
+ if (Object.values(calendar).every((value) => value === null)) {
67
66
  return "";
68
67
  }
69
- return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
68
+ return `${calendar.monday ?? "0"}${calendar.tuesday ?? "0"}${calendar.wednesday ?? "0"}${calendar.thursday ?? "0"}${calendar.friday ?? "0"}${calendar.saturday ?? "0"}${calendar.sunday ?? "0"}`;
70
69
  }
71
70
  function calendarCodeToCalendar(code) {
72
71
  const days2 = [
@@ -84,6 +83,35 @@ function calendarCodeToCalendar(code) {
84
83
  }
85
84
  return calendar;
86
85
  }
86
+ function calendarToDateList(calendar, startDate, endDate) {
87
+ if (!startDate || !endDate) {
88
+ return [];
89
+ }
90
+ const activeWeekdays = [
91
+ calendar.monday === 1 ? 1 : null,
92
+ calendar.tuesday === 1 ? 2 : null,
93
+ calendar.wednesday === 1 ? 3 : null,
94
+ calendar.thursday === 1 ? 4 : null,
95
+ calendar.friday === 1 ? 5 : null,
96
+ calendar.saturday === 1 ? 6 : null,
97
+ calendar.sunday === 1 ? 7 : null
98
+ ].filter((weekday) => weekday !== null);
99
+ if (activeWeekdays.length === 0) {
100
+ return [];
101
+ }
102
+ const activeWeekdaySet = new Set(activeWeekdays);
103
+ const dates = /* @__PURE__ */ new Set();
104
+ const date = moment(startDate.toString(), "YYYYMMDD");
105
+ const endDateMoment = moment(endDate.toString(), "YYYYMMDD");
106
+ while (date.isSameOrBefore(endDateMoment)) {
107
+ const isoWeekday = date.isoWeekday();
108
+ if (activeWeekdaySet.has(isoWeekday)) {
109
+ dates.add(parseInt(date.format("YYYYMMDD"), 10));
110
+ }
111
+ date.add(1, "day");
112
+ }
113
+ return Array.from(dates);
114
+ }
87
115
  function secondsAfterMidnight(timeString) {
88
116
  return moment.duration(timeString).asSeconds();
89
117
  }
@@ -494,7 +522,7 @@ function formatTripNameForCSV(trip, timetable) {
494
522
  // package.json
495
523
  var package_default = {
496
524
  name: "gtfs-to-html",
497
- version: "2.12.1",
525
+ version: "2.12.3",
498
526
  private: false,
499
527
  description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
500
528
  keywords: [
@@ -529,6 +557,7 @@ var package_default = {
529
557
  "dist",
530
558
  "docker",
531
559
  "examples",
560
+ "scripts",
532
561
  "views/default",
533
562
  "config-sample.json"
534
563
  ],
@@ -537,30 +566,32 @@ var package_default = {
537
566
  },
538
567
  scripts: {
539
568
  build: "tsup",
569
+ postbuild: "node scripts/postinstall.js",
540
570
  start: "node ./dist/app",
541
- prepare: "husky"
571
+ prepare: "husky && npm run build",
572
+ postinstall: "node scripts/postinstall.js"
542
573
  },
543
574
  dependencies: {
544
575
  "@maplibre/maplibre-gl-geocoder": "^1.9.1",
545
- "@turf/helpers": "^7.3.0",
546
- "@turf/simplify": "^7.3.0",
576
+ "@turf/helpers": "^7.3.1",
577
+ "@turf/simplify": "^7.3.1",
547
578
  anchorme: "^3.0.8",
548
579
  archiver: "^7.0.1",
549
580
  "cli-table": "^0.3.11",
550
581
  "css.escape": "^1.5.1",
551
582
  "csv-stringify": "^6.6.0",
552
- express: "^5.1.0",
553
- gtfs: "^4.18.1",
583
+ express: "^5.2.1",
584
+ gtfs: "^4.18.2",
554
585
  "gtfs-realtime-pbf-js-module": "^1.0.0",
555
586
  "js-beautify": "^1.15.4",
556
587
  "lodash-es": "^4.17.21",
557
- "maplibre-gl": "^5.13.0",
558
- marked: "^17.0.0",
588
+ "maplibre-gl": "^5.14.0",
589
+ marked: "^17.0.1",
559
590
  moment: "^2.30.1",
560
591
  pbf: "^4.0.1",
561
592
  "pretty-error": "^4.0.0",
562
593
  pug: "^3.0.3",
563
- puppeteer: "^24.30.0",
594
+ puppeteer: "^24.32.0",
564
595
  "sanitize-filename": "^1.6.3",
565
596
  "sanitize-html": "^2.17.0",
566
597
  sqlstring: "^2.3.3",
@@ -571,7 +602,7 @@ var package_default = {
571
602
  devDependencies: {
572
603
  "@types/archiver": "^7.0.0",
573
604
  "@types/cli-table": "^0.3.4",
574
- "@types/express": "^5.0.5",
605
+ "@types/express": "^5.0.6",
575
606
  "@types/insane": "^1.0.0",
576
607
  "@types/js-beautify": "^1.14.3",
577
608
  "@types/lodash-es": "^4.17.12",
@@ -584,7 +615,7 @@ var package_default = {
584
615
  "@types/yargs": "^17.0.35",
585
616
  husky: "^9.1.7",
586
617
  "lint-staged": "^16.2.7",
587
- prettier: "^3.6.2",
618
+ prettier: "^3.7.4",
588
619
  tsup: "^8.5.1",
589
620
  typescript: "^5.9.3"
590
621
  },
@@ -1249,9 +1280,9 @@ var getCalendarsFromTimetable = (timetable) => {
1249
1280
  }
1250
1281
  return db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
1251
1282
  };
1252
- var getCalendarDatesServiceIds = (startDate, endDate) => {
1283
+ var getCalendarDatesForDateRange = (startDate, endDate) => {
1253
1284
  const db = openDb();
1254
- const whereClauses = ["exception_type = 1"];
1285
+ const whereClauses = [];
1255
1286
  if (endDate) {
1256
1287
  whereClauses.push(`date <= ${sqlString.escape(endDate)}`);
1257
1288
  }
@@ -1259,11 +1290,11 @@ var getCalendarDatesServiceIds = (startDate, endDate) => {
1259
1290
  whereClauses.push(`date >= ${sqlString.escape(startDate)}`);
1260
1291
  }
1261
1292
  const calendarDates = db.prepare(
1262
- `SELECT DISTINCT service_id FROM calendar_dates WHERE ${whereClauses.join(
1293
+ `SELECT service_id, date, exception_type FROM calendar_dates WHERE ${whereClauses.join(
1263
1294
  " AND "
1264
1295
  )}`
1265
1296
  ).all();
1266
- return calendarDates.map((calendarDate) => calendarDate.service_id);
1297
+ return calendarDates;
1267
1298
  };
1268
1299
  var getAllStationStopIds = (stopId) => {
1269
1300
  const stops = getStops({
@@ -1474,13 +1505,49 @@ var formatTimetables = (timetables, config) => {
1474
1505
  timetable.warnings = [];
1475
1506
  const dayList = formatDays(timetable, config);
1476
1507
  const calendars = getCalendarsFromTimetable(timetable);
1477
- let serviceIds = calendars.map((calendar) => calendar.service_id);
1508
+ const serviceIds = /* @__PURE__ */ new Set();
1509
+ for (const calendar of calendars) {
1510
+ serviceIds.add(calendar.service_id);
1511
+ }
1478
1512
  if (timetable.include_exceptions === 1) {
1479
- const calendarDatesServiceIds = getCalendarDatesServiceIds(
1513
+ const calendarDates = getCalendarDatesForDateRange(
1480
1514
  timetable.start_date,
1481
1515
  timetable.end_date
1482
1516
  );
1483
- serviceIds = uniq([...serviceIds, ...calendarDatesServiceIds]);
1517
+ const calendarDateGroups = groupBy(calendarDates, "service_id");
1518
+ for (const [serviceId, calendarDateGroup] of Object.entries(
1519
+ calendarDateGroups
1520
+ )) {
1521
+ const calendar = calendars.find(
1522
+ (c) => c.service_id === serviceId
1523
+ );
1524
+ if (calendarDateGroup.some(
1525
+ (calendarDate) => calendarDate.exception_type === 1
1526
+ )) {
1527
+ serviceIds.add(serviceId);
1528
+ }
1529
+ const calendarDateGroupExceptionType2 = calendarDateGroup.filter(
1530
+ (calendarDate) => calendarDate.exception_type === 2
1531
+ );
1532
+ if (timetable.start_date && timetable.end_date && calendar && calendarDateGroupExceptionType2.length > 0) {
1533
+ const datesDuringDateRange = calendarToDateList(
1534
+ calendar,
1535
+ timetable.start_date,
1536
+ timetable.end_date
1537
+ );
1538
+ if (datesDuringDateRange.length === 0) {
1539
+ serviceIds.delete(serviceId);
1540
+ }
1541
+ const everyDateIsExcluded = datesDuringDateRange.every(
1542
+ (dateDuringDateRange) => calendarDateGroupExceptionType2.some(
1543
+ (calendarDate) => calendarDate.date === dateDuringDateRange
1544
+ )
1545
+ );
1546
+ if (everyDateIsExcluded) {
1547
+ serviceIds.delete(serviceId);
1548
+ }
1549
+ }
1550
+ }
1484
1551
  }
1485
1552
  Object.assign(timetable, {
1486
1553
  noServiceSymbolUsed: false,
@@ -1498,7 +1565,7 @@ var formatTimetables = (timetables, config) => {
1498
1565
  noPickupSymbol: config.noPickupSymbol,
1499
1566
  interpolatedStopSymbol: config.interpolatedStopSymbol,
1500
1567
  orientation: timetable.orientation || config.defaultOrientation,
1501
- service_ids: serviceIds,
1568
+ service_ids: Array.from(serviceIds),
1502
1569
  dayList,
1503
1570
  dayListLong: formatDaysLong(dayList, config)
1504
1571
  });
@@ -2319,20 +2386,23 @@ async function getConfig(argv2) {
2319
2386
  }
2320
2387
  return config;
2321
2388
  }
2322
- function getPathToViewsFolder(config) {
2323
- if (config.templatePath) {
2324
- return untildify(config.templatePath);
2325
- }
2389
+ function getPathToThisModuleFolder() {
2326
2390
  const __dirname = dirname(fileURLToPath(import.meta.url));
2327
- let viewsFolderPath;
2391
+ let distFolderPath;
2328
2392
  if (__dirname.endsWith("/dist/bin") || __dirname.endsWith("/dist/app")) {
2329
- viewsFolderPath = resolve(__dirname, "../../views/default");
2393
+ distFolderPath = resolve(__dirname, "../../");
2330
2394
  } else if (__dirname.endsWith("/dist")) {
2331
- viewsFolderPath = resolve(__dirname, "../views/default");
2395
+ distFolderPath = resolve(__dirname, "../");
2332
2396
  } else {
2333
- viewsFolderPath = resolve(__dirname, "../../views/default");
2397
+ distFolderPath = resolve(__dirname, "../../");
2334
2398
  }
2335
- return viewsFolderPath;
2399
+ return distFolderPath;
2400
+ }
2401
+ function getPathToViewsFolder(config) {
2402
+ if (config.templatePath) {
2403
+ return untildify(config.templatePath);
2404
+ }
2405
+ return join(getPathToThisModuleFolder(), "views/default");
2336
2406
  }
2337
2407
  function getPathToTemplateFile(templateFileName, config) {
2338
2408
  const fullTemplateFileName = config.noHead !== true ? `${templateFileName}_full.pug` : `${templateFileName}.pug`;
@@ -2365,6 +2435,7 @@ async function prepDirectory(outputPath, config) {
2365
2435
  }
2366
2436
  async function copyStaticAssets(config, outputPath) {
2367
2437
  const viewsFolderPath = getPathToViewsFolder(config);
2438
+ const thisModuleFolderPath = getPathToThisModuleFolder();
2368
2439
  const foldersToCopy = ["css", "js", "img"];
2369
2440
  for (const folder of foldersToCopy) {
2370
2441
  if (await access(join(viewsFolderPath, folder)).then(() => true).catch(() => false)) {
@@ -2375,70 +2446,43 @@ async function copyStaticAssets(config, outputPath) {
2375
2446
  }
2376
2447
  if (config.hasGtfsRealtimeVehiclePositions || config.hasGtfsRealtimeTripUpdates || config.hasGtfsRealtimeAlerts) {
2377
2448
  await copyFile(
2378
- join(
2379
- dirname(findPackageJSON("pbf", import.meta.url)),
2380
- "dist/pbf.js"
2381
- ),
2449
+ join(thisModuleFolderPath, "dist/frontend_libraries/pbf.js"),
2382
2450
  join(outputPath, "js/pbf.js")
2383
2451
  );
2384
2452
  await copyFile(
2385
2453
  join(
2386
- dirname(
2387
- findPackageJSON(
2388
- "gtfs-realtime-pbf-js-module",
2389
- import.meta.url
2390
- )
2391
- ),
2392
- "gtfs-realtime.browser.proto.js"
2454
+ thisModuleFolderPath,
2455
+ "dist/frontend_libraries/gtfs-realtime.browser.proto.js"
2393
2456
  ),
2394
2457
  join(outputPath, "js/gtfs-realtime.browser.proto.js")
2395
2458
  );
2396
2459
  }
2397
2460
  if (config.hasGtfsRealtimeAlerts) {
2398
2461
  await copyFile(
2399
- join(
2400
- dirname(findPackageJSON("anchorme", import.meta.url)),
2401
- "dist/browser/anchorme.min.js"
2402
- ),
2462
+ join(thisModuleFolderPath, "dist/frontend_libraries/anchorme.min.js"),
2403
2463
  join(outputPath, "js/anchorme.min.js")
2404
2464
  );
2405
2465
  }
2406
2466
  if (config.showMap) {
2407
2467
  await copyFile(
2408
- join(
2409
- dirname(findPackageJSON("maplibre-gl", import.meta.url)),
2410
- "dist/maplibre-gl.js"
2411
- ),
2468
+ join(thisModuleFolderPath, "dist/frontend_libraries/maplibre-gl.js"),
2412
2469
  join(outputPath, "js/maplibre-gl.js")
2413
2470
  );
2414
2471
  await copyFile(
2415
- join(
2416
- dirname(findPackageJSON("maplibre-gl", import.meta.url)),
2417
- "dist/maplibre-gl.css"
2418
- ),
2472
+ join(thisModuleFolderPath, "dist/frontend_libraries/maplibre-gl.css"),
2419
2473
  join(outputPath, "css/maplibre-gl.css")
2420
2474
  );
2421
2475
  await copyFile(
2422
2476
  join(
2423
- dirname(
2424
- findPackageJSON(
2425
- "@maplibre/maplibre-gl-geocoder",
2426
- import.meta.url
2427
- )
2428
- ),
2429
- "dist/maplibre-gl-geocoder.js"
2477
+ thisModuleFolderPath,
2478
+ "dist/frontend_libraries/maplibre-gl-geocoder.js"
2430
2479
  ),
2431
2480
  join(outputPath, "js/maplibre-gl-geocoder.js")
2432
2481
  );
2433
2482
  await copyFile(
2434
2483
  join(
2435
- dirname(
2436
- findPackageJSON(
2437
- "@maplibre/maplibre-gl-geocoder",
2438
- import.meta.url
2439
- )
2440
- ),
2441
- "dist/maplibre-gl-geocoder.css"
2484
+ thisModuleFolderPath,
2485
+ "dist/frontend_libraries/maplibre-gl-geocoder.css"
2442
2486
  ),
2443
2487
  join(outputPath, "css/maplibre-gl-geocoder.css")
2444
2488
  );