aprs.me/scripts/build-vendor-bundle.js
Graham McIntire f4bc55ed39
Optimize JavaScript delivery by creating local vendor bundles
- Download external libraries locally to reduce CDN dependencies
- Create combined vendor bundles for JavaScript and CSS
- Replace CDN script tags with local bundles in layout template
- Add build scripts for vendor bundle creation and optimization
- Configure esbuild to process vendor bundles separately
- Total bundle size: ~512KB JS + 17KB CSS (optimized)

Benefits:
- Reduced external requests and CDN dependencies
- Better caching control and reliability
- Eliminated CORS and SRI integrity issues
- Faster page loads with bundled resources

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-25 13:56:12 -05:00

158 lines
No EOL
4.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Script to download, minify, and bundle JavaScript libraries locally
* This reduces external requests and allows us to remove unused code
*/
const fs = require('fs');
const path = require('path');
const https = require('https');
// URLs of the libraries we need
const libraries = [
{
name: 'leaflet',
url: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
minUrl: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'
},
{
name: 'leaflet-heat',
url: 'https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js',
minUrl: 'https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js'
},
{
name: 'leaflet-markercluster',
url: 'https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js',
minUrl: 'https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js'
},
{
name: 'overlapping-marker-spiderfier',
url: 'https://unpkg.com/overlapping-marker-spiderfier-leaflet@0.2.7/dist/oms.js',
minUrl: 'https://unpkg.com/overlapping-marker-spiderfier-leaflet@0.2.7/dist/oms.js'
},
{
name: 'chart-js',
url: 'https://unpkg.com/chart.js@4.5.0/dist/chart.umd.js',
minUrl: 'https://unpkg.com/chart.js@4.5.0/dist/chart.umd.js'
},
{
name: 'chartjs-adapter-date-fns',
url: 'https://unpkg.com/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js',
minUrl: 'https://unpkg.com/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js'
},
{
name: 'topbar',
url: 'https://unpkg.com/topbar@3.0.0/topbar.min.js',
minUrl: 'https://unpkg.com/topbar@3.0.0/topbar.min.js'
},
{
name: 'leaflet-vectorgrid',
url: 'https://unpkg.com/leaflet.vectorgrid@1.3.0/dist/Leaflet.VectorGrid.bundled.js',
minUrl: 'https://unpkg.com/leaflet.vectorgrid@1.3.0/dist/Leaflet.VectorGrid.bundled.js'
}
];
// CSS files we need
const cssFiles = [
{
name: 'leaflet',
url: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'
},
{
name: 'leaflet-markercluster',
url: 'https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css'
},
{
name: 'leaflet-markercluster-default',
url: 'https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css'
}
];
// Download function
function downloadFile(url, filename) {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(filename);
https.get(url, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`Failed to download ${url}: ${response.statusCode}`));
return;
}
response.pipe(file);
file.on('finish', () => {
file.close();
console.log(`Downloaded: ${filename}`);
resolve();
});
}).on('error', (err) => {
fs.unlink(filename, () => {}); // Delete the file on error
reject(err);
});
});
}
async function buildVendorBundle() {
// Create directories if they don't exist
const jsDir = path.join(__dirname, '..', 'assets', 'vendor', 'js');
const cssDir = path.join(__dirname, '..', 'assets', 'vendor', 'css');
fs.mkdirSync(jsDir, { recursive: true });
fs.mkdirSync(cssDir, { recursive: true });
// Download JavaScript libraries
console.log('Downloading JavaScript libraries...');
for (const lib of libraries) {
const filename = path.join(jsDir, `${lib.name}.js`);
try {
await downloadFile(lib.minUrl, filename);
} catch (error) {
console.error(`Failed to download ${lib.name}:`, error.message);
}
}
// Download CSS files
console.log('Downloading CSS files...');
for (const css of cssFiles) {
const filename = path.join(cssDir, `${css.name}.css`);
try {
await downloadFile(css.url, filename);
} catch (error) {
console.error(`Failed to download ${css.name}:`, error.message);
}
}
// Create a combined vendor bundle
console.log('Creating combined vendor bundle...');
let combinedJS = '';
let combinedCSS = '';
// Combine JavaScript files
for (const lib of libraries) {
const filename = path.join(jsDir, `${lib.name}.js`);
if (fs.existsSync(filename)) {
const content = fs.readFileSync(filename, 'utf8');
combinedJS += `\n/* ${lib.name} */\n${content}\n`;
}
}
// Combine CSS files
for (const css of cssFiles) {
const filename = path.join(cssDir, `${css.name}.css`);
if (fs.existsSync(filename)) {
const content = fs.readFileSync(filename, 'utf8');
combinedCSS += `\n/* ${css.name} */\n${content}\n`;
}
}
// Write combined files
fs.writeFileSync(path.join(jsDir, 'vendor-bundle.js'), combinedJS);
fs.writeFileSync(path.join(cssDir, 'vendor-bundle.css'), combinedCSS);
console.log('✅ Vendor bundle created successfully!');
console.log(`JavaScript bundle: ${(combinedJS.length / 1024).toFixed(2)} KB`);
console.log(`CSS bundle: ${(combinedCSS.length / 1024).toFixed(2)} KB`);
}
// Run the build
buildVendorBundle().catch(console.error);