From db257efbfbefba96f175c2e2beb2b14ec9097c49 Mon Sep 17 00:00:00 2001 From: HighImKevin Date: Sun, 11 Oct 2026 02:34:55 -0400 Subject: [PATCH] Ensure KefinTweaks urls are properly normalized so that duplicate scripts are never loaded --- scripts/kefinTweaks-loader.js | 53 +++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/scripts/kefinTweaks-loader.js b/scripts/kefinTweaks-loader.js index 669c97d..197e853 100644 --- a/scripts/kefinTweaks-loader.js +++ b/scripts/kefinTweaks-loader.js @@ -671,11 +671,17 @@ return normalizeRoot(cfg.kefinTweaksRootResolved || cfg.kefinTweaksRoot || ''); } - function resolveAssetUrl(root, relativePath) { + /** + * Join kefin root + scripts/ + relativePath, collapsing . / .. for site-absolute roots. + * Shared by live resolveAssetUrl and the baked KefinTweaks-injector joinAssetUrl. + */ + function joinAssetUrlForRoot(resolvedRoot, relativePath) { if (!relativePath) return null; if (/^https?:\/\//i.test(relativePath)) return relativePath; - const normalizedRoot = normalizeRoot(root); + const normalizedRoot = normalizeRoot(resolvedRoot); + if (!normalizedRoot) return null; + const base = normalizedRoot + 'scripts/'; const joined = (base + relativePath).replace(/\\/g, '/'); @@ -684,7 +690,7 @@ try { return new URL(relativePath, base).href; } catch (e) { - /* fall through */ + /* fall through to collapse */ } } @@ -704,6 +710,39 @@ return isSiteAbsolute ? '/' + resolved : resolved; } + function resolveAssetUrl(root, relativePath) { + return joinAssetUrlForRoot(root, relativePath); + } + + /** + * ES5 joinAssetUrl source for buildInjectorScript (closes over resolvedRoot). + * Must stay algorithmically identical to joinAssetUrlForRoot. + */ + function getJoinAssetUrlRuntimeSource() { + return [ + ' function joinAssetUrl(relativePath) {', + ' if (!relativePath) return null;', + ' if (/^https?:\\/\\//i.test(relativePath)) return relativePath;', + ' var base = resolvedRoot + \'scripts/\';', + ' var joined = (base + relativePath).replace(/\\\\/g, \'/\');', + ' if (/^https?:\\/\\//i.test(resolvedRoot)) {', + ' try { return new URL(relativePath, base).href; } catch (e) {}', + ' }', + ' var isSiteAbsolute = joined.charAt(0) === \'/\';', + ' var parts = joined.split(\'/\');', + ' var stack = [];', + ' for (var pi = 0; pi < parts.length; pi++) {', + ' var p = parts[pi];', + ' if (!p || p === \'.\') continue;', + ' if (p === \'..\') { stack.pop(); continue; }', + ' stack.push(p);', + ' }', + ' var resolved = stack.join(\'/\');', + ' return isSiteAbsolute ? \'/\' + resolved : resolved;', + ' }' + ].join('\n'); + } + function expandScriptToAssets(scriptDef, root, urlSuffix, thirdParty) { const assets = []; const suffix = urlSuffix || ''; @@ -989,12 +1028,7 @@ ' var resolvedRoot = String(cfg.kefinTweaksRootResolved || cfg.kefinTweaksRoot || \'\').trim();', ' if (!resolvedRoot) return;', ' if (resolvedRoot.charAt(resolvedRoot.length - 1) !== \'/\') resolvedRoot += \'/\';', - ' function joinAssetUrl(relativePath) {', - ' if (!relativePath) return null;', - ' if (/^https?:\\/\\//i.test(relativePath)) return relativePath;', - ' var base = resolvedRoot + \'scripts/\';', - ' try { return new URL(relativePath, base).href; } catch (e) { return base + relativePath; }', - ' }', + getJoinAssetUrlRuntimeSource(), ' window.KefinTweaksScriptsPreloaded = true;', ' window.KefinTweaksInjectorStamp = ' + stampJson + ';', ' var SCRIPT_DEFINITIONS_BAKED = ' + defsJson + ';', @@ -1260,6 +1294,7 @@ mergeEnabledScripts: mergeEnabledScripts, normalizeRoot: normalizeRoot, getResolvedKefinRoot: getResolvedKefinRoot, + joinAssetUrlForRoot: joinAssetUrlForRoot, resolveAssetUrl: resolveAssetUrl, buildLoadPlan: buildLoadPlan, buildInjectorScript: buildInjectorScript,