From 63a87f3be152d32371f040560f65232b161bfccf Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 21 Jul 2025 12:32:39 -0500 Subject: [PATCH] Fix Docker build JavaScript import errors with vendor bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed the esbuild errors during Docker build where npm packages couldn't be resolved. The issue was caused by node_modules being excluded from the Docker build context in .dockerignore. Implemented a proper vendor bundle strategy following Phoenix 1.7+ patterns: - Created vendor.js that bundles all npm dependencies (Leaflet, Chart.js, topbar) and their CSS files into a single pre-built bundle - Added vendor esbuild profile in config.exs with proper CSS/image loaders - Updated mix.exs to build vendor bundle before app bundle in assets.deploy - Modified JavaScript imports to use globally loaded libraries from vendor bundle instead of importing from node_modules - Added vendor.js script tag to root.html.heex before app.js This approach avoids Docker build failures by pre-bundling all vendor dependencies, eliminating the need for node_modules during production builds. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- assets/js/app.js | 3 +- assets/js/features/weather_charts.ts | 9 ++---- assets/js/map.ts | 15 ++-------- assets/js/vendor.js | 28 +++++++++++++++++++ config/config.exs | 6 ++++ .../components/layouts/root.html.heex | 2 ++ mix.exs | 1 + 7 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 assets/js/vendor.js diff --git a/assets/js/app.js b/assets/js/app.js index 81f84ec..8d8a764 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -20,7 +20,8 @@ import "phoenix_html"; // Establish Phoenix Socket and LiveView configuration. import { Socket } from "phoenix"; import { LiveSocket } from "phoenix_live_view"; -import topbar from "topbar"; +// topbar is loaded globally from vendor bundle +const topbar = window.topbar; // Sentry initialization happens via the loader script in the HTML // Configure additional Sentry settings if needed diff --git a/assets/js/features/weather_charts.ts b/assets/js/features/weather_charts.ts index e9cc2ce..a3d8e37 100644 --- a/assets/js/features/weather_charts.ts +++ b/assets/js/features/weather_charts.ts @@ -1,13 +1,10 @@ -// Import Chart.js and date adapter -import Chart from 'chart.js/auto'; -import 'chartjs-adapter-date-fns'; +// Chart.js and date adapter are loaded globally from vendor bundle +const Chart = window.Chart; + import type { ChartConfiguration, ChartType } from 'chart.js'; import type { WeatherChartDataset, YAxisOptions } from '../types/chart-types'; import type { HandleEventFunction } from '../types/events'; -// Make Chart globally available for legacy code -window.Chart = Chart; - // Declare global Chart object declare global { interface Window { diff --git a/assets/js/map.ts b/assets/js/map.ts index 1d06fee..d957844 100644 --- a/assets/js/map.ts +++ b/assets/js/map.ts @@ -12,19 +12,10 @@ import type { HeatLayer, MarkerClusterGroup, OverlappingMarkerSpiderfier, Marker import type { APRSMarker } from './types/marker-extensions'; import type { BaseEventPayload } from './types/events'; -// Import Leaflet and plugins -import L from 'leaflet'; -import 'leaflet.heat'; -import 'leaflet.markercluster'; -import 'overlapping-marker-spiderfier-leaflet'; +// Leaflet and plugins are loaded globally from vendor bundle +const L = window.L; -// Import CSS files -import 'leaflet/dist/leaflet.css'; -import 'leaflet.markercluster/dist/MarkerCluster.css'; -import 'leaflet.markercluster/dist/MarkerCluster.Default.css'; - -// Make Leaflet globally available for legacy code -window.L = L; +// CSS files are bundled in vendor.js // The OverlappingMarkerSpiderfier is attached to window by the UMD module declare global { diff --git a/assets/js/vendor.js b/assets/js/vendor.js new file mode 100644 index 0000000..c126a45 --- /dev/null +++ b/assets/js/vendor.js @@ -0,0 +1,28 @@ +// Vendor bundle for all third-party dependencies +// This file bundles all npm packages to avoid resolution issues during Docker builds + +// Import CSS files first +import 'leaflet/dist/leaflet.css'; +import 'leaflet.markercluster/dist/MarkerCluster.css'; +import 'leaflet.markercluster/dist/MarkerCluster.Default.css'; + +// Export Leaflet and plugins +import * as L from 'leaflet'; +import 'leaflet.heat'; +import 'leaflet.markercluster'; +import 'overlapping-marker-spiderfier-leaflet'; + +// Export Chart.js and adapter +import Chart from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; + +// Export topbar +import topbar from 'topbar'; + +// Make libraries available globally +window.L = L; +window.Chart = Chart; +window.topbar = topbar; + +// Export for ES modules +export { L, Chart, topbar }; \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index 1bce9d3..4252309 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/* --loader:.css=css --loader:.png=file --loader:.svg=file), 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 --loader:.css=css --loader:.png=file --loader:.svg=file), + cd: Path.expand("../assets", __DIR__), + env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] config :gettext, :plural_forms, GettextPseudolocalize.Plural diff --git a/lib/aprsme_web/components/layouts/root.html.heex b/lib/aprsme_web/components/layouts/root.html.heex index 0ea1280..c991cd5 100644 --- a/lib/aprsme_web/components/layouts/root.html.heex +++ b/lib/aprsme_web/components/layouts/root.html.heex @@ -26,6 +26,8 @@ {assigns[:page_title] || "Aprs"} +