const SCRIPT_SRC_PATTERN = /]*)\bsrc\s*=\s*(['"])([^'"]+)\2([^>]*)>/gi; const STYLESHEET_HREF_PATTERN = /]*)\brel\s*=\s*(['"])stylesheet\2([^>]*)\bhref\s*=\s*(['"])([^'"]+)\4([^>]*)>/gi; const STYLESHEET_HREF_ALT_PATTERN = /]*)\bhref\s*=\s*(['"])([^'"]+)\2([^>]*)\brel\s*=\s*(['"])stylesheet\5([^>]*)>/gi; export const KNOWN_PLATFORM_SCRIPT_ALIASES = [ { id: 'chart.js', target: '/assets/chart.umd.min.js', patterns: [ /cdn\.jsdelivr\.net\/npm\/chart\.js(?:@[^/]+)?\/dist\/chart\.(?:umd\.)?min\.js/i, /cdnjs\.cloudflare\.com\/ajax\/libs\/Chart\.js\/[^/]+\/chart\.(?:umd\.)?min\.js/i, /unpkg\.com\/chart\.js(?:@[^/]+)?\/dist\/chart\.(?:umd\.)?min\.js/i, ], }, { id: 'leaflet', target: '/assets/leaflet/leaflet.js', patterns: [ /cdn\.jsdelivr\.net\/npm\/leaflet(?:@[^/]+)?\/dist\/leaflet\.js/i, /cdnjs\.cloudflare\.com\/ajax\/libs\/leaflet\/[^/]+\/leaflet\.js/i, /unpkg\.com\/leaflet(?:@[^/]+)?\/dist\/leaflet\.js/i, ], }, ]; export const KNOWN_PLATFORM_STYLESHEET_ALIASES = [ { id: 'leaflet', target: '/assets/leaflet/leaflet.css', patterns: [ /cdn\.jsdelivr\.net\/npm\/leaflet(?:@[^/]+)?\/dist\/leaflet\.css/i, /cdnjs\.cloudflare\.com\/ajax\/libs\/leaflet\/[^/]+\/leaflet\.css/i, /unpkg\.com\/leaflet(?:@[^/]+)?\/dist\/leaflet\.css/i, ], }, ]; function normalizeScriptUrl(value) { return String(value ?? '').trim().replace(/&/g, '&'); } export function isExternalScriptSrc(src) { const normalized = normalizeScriptUrl(src); if (!normalized) return false; if (/^https?:\/\//i.test(normalized)) return true; if (/^\/\//.test(normalized)) return true; return false; } function resolveKnownPlatformTarget(src, aliases) { const normalized = normalizeScriptUrl(src); if (!isExternalScriptSrc(normalized)) return null; const probe = normalized.startsWith('//') ? `https:${normalized}` : normalized; for (const alias of aliases) { if (alias.patterns.some((pattern) => pattern.test(probe))) { return alias.target; } } return null; } function resolveKnownPlatformScriptTarget(src) { return resolveKnownPlatformTarget(src, KNOWN_PLATFORM_SCRIPT_ALIASES); } function resolveKnownPlatformStylesheetTarget(href) { return resolveKnownPlatformTarget(href, KNOWN_PLATFORM_STYLESHEET_ALIASES); } export function findExternalScriptSources(html) { const sources = []; const seen = new Set(); for (const match of String(html ?? '').matchAll(SCRIPT_SRC_PATTERN)) { const src = normalizeScriptUrl(match[3]); if (!isExternalScriptSrc(src) || seen.has(src)) continue; seen.add(src); sources.push(src); } return sources; } export function rewriteKnownCdnScriptSources(html) { const rewrites = []; const next = String(html ?? '').replace( SCRIPT_SRC_PATTERN, (full, before, quote, src, after) => { const normalized = normalizeScriptUrl(src); const target = resolveKnownPlatformScriptTarget(normalized); if (!target) return full; rewrites.push({ from: normalized, to: target }); return ``; }, ); return { html: next, rewrittenCount: rewrites.length, rewrites, }; } export function rewriteKnownCdnStylesheetSources(html) { const rewrites = []; let next = String(html ?? ''); const replaceStylesheet = (full, before, quote, href, after) => { const normalized = normalizeScriptUrl(href); const target = resolveKnownPlatformStylesheetTarget(normalized); if (!target) return full; rewrites.push({ from: normalized, to: target }); return ``; }; next = next.replace( STYLESHEET_HREF_PATTERN, (full, before, _relQuote, _middle, hrefQuote, href, after) => replaceStylesheet(full, before, hrefQuote, href, after), ); next = next.replace( STYLESHEET_HREF_ALT_PATTERN, (full, before, hrefQuote, href, _middle, _relQuote, after) => replaceStylesheet(full, before, hrefQuote, href, after), ); return { html: next, rewrittenCount: rewrites.length, rewrites, }; } export function rewriteKnownCdnAssetSources(html) { const scripts = rewriteKnownCdnScriptSources(html); const stylesheets = rewriteKnownCdnStylesheetSources(scripts.html); return { html: stylesheets.html, rewrittenCount: scripts.rewrittenCount + stylesheets.rewrittenCount, rewrites: [...scripts.rewrites, ...stylesheets.rewrites], }; }