From 7dc6c616a22d599f78c49ae7c2f686013ef70c59 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 21 Jul 2025 09:52:39 -0500 Subject: [PATCH] Bundle all vendor JavaScript files locally instead of loading from CDNs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change eliminates all external CDN dependencies by bundling JavaScript libraries locally using esbuild. All vendor files are now served from the application's own assets, improving reliability and privacy. Changes: - Created assets/js/vendor.js to bundle all external libraries - Added vendor bundle configuration to esbuild in config files - Updated all templates to use local vendor.js instead of CDN scripts - Created download-vendor-files.sh script to fetch vendor libraries - Added vendor files to .gitignore (they're downloaded during setup) - Updated CSS imports to include vendor stylesheets - Added vendor build step to assets.deploy mix task Bundled libraries: - Leaflet 1.9.4 (mapping library) - Leaflet.heat 0.2.0 (heatmap plugin) - Leaflet.markercluster 1.5.3 (marker clustering) - OverlappingMarkerSpiderfier 0.2.6 (overlapping marker handling) - Chart.js 4.5.0 (charting library) - chartjs-adapter-date-fns 3.0.0 (date adapter for Chart.js) All functionality tested and working correctly with local bundles. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 4 ++ assets/css/app.css | 5 ++ assets/download-vendor-files.sh | 43 ++++++++++++++ assets/js/vendor.js | 23 ++++++++ assets/vendor/README.md | 56 +++++++++++++++++++ config/config.exs | 6 ++ config/dev.exs | 6 ++ .../components/layouts/root.html.heex | 37 +----------- lib/aprsme_web/live/map_live/index.ex | 19 +------ .../live/weather_live/callsign_view.html.heex | 7 +-- mix.exs | 1 + 11 files changed, 148 insertions(+), 59 deletions(-) create mode 100755 assets/download-vendor-files.sh create mode 100644 assets/js/vendor.js create mode 100644 assets/vendor/README.md diff --git a/.gitignore b/.gitignore index d5dd622..d92d502 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,10 @@ aprsme-*.tar # Ignore assets that are produced by build tools. /priv/static/assets/ +# Ignore vendor JavaScript/CSS files (downloaded from CDNs) +/assets/vendor/*.js +/assets/vendor/*.css + # Ignore digested assets cache. /priv/static/cache_manifest.json diff --git a/assets/css/app.css b/assets/css/app.css index e084e31..8040096 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -3,6 +3,11 @@ @source "../js"; @source "../../lib/aprsme_web"; +/* Import vendor CSS files */ +@import "../vendor/leaflet.css"; +@import "../vendor/MarkerCluster.css"; +@import "../vendor/MarkerCluster.Default.css"; + /* A Tailwind plugin that makes "hero-#{ICON}" classes available. The heroicons installation itself is managed by your mix.exs */ @plugin "../vendor/heroicons"; diff --git a/assets/download-vendor-files.sh b/assets/download-vendor-files.sh new file mode 100755 index 0000000..fe5efdb --- /dev/null +++ b/assets/download-vendor-files.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Script to download vendor JavaScript and CSS files +# Run this from the project root: ./assets/download-vendor-files.sh + +set -e + +echo "Downloading vendor files..." + +# Ensure vendor directory exists +mkdir -p assets/vendor + +cd assets/vendor + +# Download JavaScript files +echo "Downloading Leaflet..." +curl -L -o leaflet.js https://unpkg.com/leaflet@1.9.4/dist/leaflet.js + +echo "Downloading Leaflet Heat..." +curl -L -o leaflet-heat.js https://cdn.jsdelivr.net/npm/leaflet.heat@0.2.0/dist/leaflet-heat.js + +echo "Downloading Chart.js..." +curl -L -o chart.umd.js https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js + +echo "Downloading Chart.js date adapter..." +curl -L -o chartjs-adapter-date-fns.bundle.min.js https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js + +echo "Downloading Leaflet MarkerCluster..." +curl -L -o leaflet.markercluster.js https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js + +echo "Downloading OverlappingMarkerSpiderfier..." +curl -L -o oms.min.js https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier-Leaflet/0.2.6/oms.min.js + +# Download CSS files +echo "Downloading Leaflet CSS..." +curl -L -o leaflet.css https://unpkg.com/leaflet@1.9.4/dist/leaflet.css + +echo "Downloading MarkerCluster CSS..." +curl -L -o MarkerCluster.css https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css +curl -L -o MarkerCluster.Default.css https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css + +echo "All vendor files downloaded successfully!" +echo "Run 'mix esbuild vendor' to build the vendor bundle." \ No newline at end of file diff --git a/assets/js/vendor.js b/assets/js/vendor.js new file mode 100644 index 0000000..f198665 --- /dev/null +++ b/assets/js/vendor.js @@ -0,0 +1,23 @@ +// Vendor libraries bundle +// This file loads all external libraries and makes them available globally + +// Load Leaflet first (required by plugins) +import '../vendor/leaflet.js'; + +// Load Leaflet plugins (they expect window.L to exist) +import '../vendor/leaflet-heat.js'; +import '../vendor/leaflet.markercluster.js'; +import '../vendor/oms.min.js'; + +// Load Chart.js +import '../vendor/chart.umd.js'; + +// Load Chart.js adapter (requires Chart to be loaded first) +import '../vendor/chartjs-adapter-date-fns.bundle.min.js'; + +// Ensure global availability +if (typeof window !== 'undefined') { + // Leaflet should already be on window.L from the library + // Chart should already be on window.Chart from the library + console.log('Vendor libraries loaded: Leaflet', !!window.L, 'Chart.js', !!window.Chart); +} \ No newline at end of file diff --git a/assets/vendor/README.md b/assets/vendor/README.md new file mode 100644 index 0000000..1b6d44c --- /dev/null +++ b/assets/vendor/README.md @@ -0,0 +1,56 @@ +# Vendor JavaScript Libraries + +This directory contains third-party JavaScript and CSS libraries that are bundled locally instead of loading from CDNs. + +## Required Files + +The following files need to be downloaded: + +### JavaScript Files +- `leaflet.js` - from https://unpkg.com/leaflet@1.9.4/dist/leaflet.js +- `leaflet-heat.js` - from https://cdn.jsdelivr.net/npm/leaflet.heat@0.2.0/dist/leaflet-heat.js +- `chart.umd.js` - from https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js +- `chartjs-adapter-date-fns.bundle.min.js` - from https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js +- `leaflet.markercluster.js` - from https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js +- `oms.min.js` - from https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier-Leaflet/0.2.6/oms.min.js + +### CSS Files +- `leaflet.css` - from https://unpkg.com/leaflet@1.9.4/dist/leaflet.css +- `MarkerCluster.css` - from https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css +- `MarkerCluster.Default.css` - from https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css + +## Download Script + +To download all vendor files, run: + +```bash +# From the project root +cd assets/vendor + +# Download JavaScript files +curl -L -o leaflet.js https://unpkg.com/leaflet@1.9.4/dist/leaflet.js +curl -L -o leaflet-heat.js https://cdn.jsdelivr.net/npm/leaflet.heat@0.2.0/dist/leaflet-heat.js +curl -L -o chart.umd.js https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js +curl -L -o chartjs-adapter-date-fns.bundle.min.js https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js +curl -L -o leaflet.markercluster.js https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js +curl -L -o oms.min.js https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier-Leaflet/0.2.6/oms.min.js + +# Download CSS files +curl -L -o leaflet.css https://unpkg.com/leaflet@1.9.4/dist/leaflet.css +curl -L -o MarkerCluster.css https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css +curl -L -o MarkerCluster.Default.css https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css +``` + +## Building + +The vendor files are bundled using esbuild: + +```bash +# Build vendor bundle +mix esbuild vendor + +# Build with minification (for production) +mix esbuild vendor --minify +``` + +The vendor bundle is automatically included in the asset deployment pipeline. \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index 9830370..664f0a0 100644 --- a/config/config.exs +++ b/config/config.exs @@ -85,6 +85,12 @@ config :esbuild, ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --external:chart.js/auto --define:global.L=window.L), cd: Path.expand("../assets", __DIR__), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} + ], + vendor: [ + args: + ~w(js/vendor.js --bundle --target=es2017 --outdir=../priv/static/assets --minify), + cd: Path.expand("../assets", __DIR__), + env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] config :gettext, :plural_forms, GettextPseudolocalize.Plural diff --git a/config/dev.exs b/config/dev.exs index 37809d2..6b3debe 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -53,6 +53,12 @@ config :aprsme, AprsmeWeb.Endpoint, :default, ~w(--sourcemap=inline --watch --loader:.ts=ts) ]}, + esbuild_vendor: + {Esbuild, :install_and_run, + [ + :vendor, + ~w(--sourcemap=inline --watch) + ]}, tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]} ] diff --git a/lib/aprsme_web/components/layouts/root.html.heex b/lib/aprsme_web/components/layouts/root.html.heex index e583c06..f111dc2 100644 --- a/lib/aprsme_web/components/layouts/root.html.heex +++ b/lib/aprsme_web/components/layouts/root.html.heex @@ -26,41 +26,8 @@ {assigns[:page_title] || "Aprs"} - - - <%!-- Load Leaflet.heat after Leaflet is loaded --%> - - - diff --git a/lib/aprsme_web/live/map_live/index.ex b/lib/aprsme_web/live/map_live/index.ex index c8cc2d6..afb8fb7 100644 --- a/lib/aprsme_web/live/map_live/index.ex +++ b/lib/aprsme_web/live/map_live/index.ex @@ -774,24 +774,7 @@ defmodule AprsmeWeb.MapLive.Index do @impl true def render(assigns) do ~H""" - - - - - - + <%!-- All vendor libraries are now loaded from vendor.js bundle --%>