const SCRIPT_SRC_PATTERN = /]*)\bsrc\s*=\s*(['"])([^'"]+)\2([^>]*)>/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, ], }, ]; 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 resolveKnownPlatformScriptTarget(src) { const normalized = normalizeScriptUrl(src); if (!isExternalScriptSrc(normalized)) return null; const probe = normalized.startsWith('//') ? `https:${normalized}` : normalized; for (const alias of KNOWN_PLATFORM_SCRIPT_ALIASES) { if (alias.patterns.some((pattern) => pattern.test(probe))) { return alias.target; } } return null; } 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, }; }