-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Carson Yang <[email protected]>
- Loading branch information
1 parent
3f088bc
commit 8d17f96
Showing
9 changed files
with
205 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"schedule": "daily", // daily|weekly|monthly | ||
"ignoredFiles": [ | ||
"*.svg", | ||
"packages/*", | ||
"projects/*", | ||
], | ||
"minKBReduced": 200, // delay new prs until size reduction meets this threshold (default to 10) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,5 +105,6 @@ | |
{{- end -}} | ||
{{- end -}} | ||
<!-- change --> | ||
<link rel="stylesheet" href="https://jsdelivr.icloudnative.io/npm/[email protected]/style.css" /> | ||
<script defer type="text/javascript" src="{{ "js/jsdelivr-auto-fallback.js" | absURL }}"></script> | ||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/style.css" /> | ||
</head> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
((document) => { | ||
'use strict'; | ||
let fastNode; | ||
let failed; | ||
let isRunning; | ||
const DEST_LIST = [ | ||
'cdn.jsdelivr.net', | ||
'jsd.cdn.zzko.cn', | ||
'cdn.jsdelivr.us', | ||
'jsd.onmicrosoft.cn', | ||
'fastly.jsdelivr.net', | ||
'gcore.jsdelivr.net' | ||
]; | ||
const PREFIX = '//'; | ||
const SOURCE = DEST_LIST[0]; | ||
const starTime = Date.now(); | ||
const TIMEOUT = 2000; | ||
const STORE_KEY = 'jsdelivr-auto-fallback'; | ||
const TEST_PATH = '/gh/PipecraftNet/jsdelivr-auto-fallback@main/empty.css?'; | ||
const shouldReplace = (text) => text && text.includes(PREFIX + SOURCE); | ||
const replace = (text) => text.replace(PREFIX + SOURCE, PREFIX + fastNode); | ||
const setTimeout = window.setTimeout; | ||
const $ = document.querySelectorAll.bind(document); | ||
|
||
const replaceElementSrc = () => { | ||
let element; | ||
let value; | ||
for (element of $('link[rel="stylesheet"]')) { | ||
value = element.href; | ||
if (shouldReplace(value) && !value.includes(TEST_PATH)) { | ||
element.href = replace(value); | ||
} | ||
} | ||
|
||
for (element of $('script')) { | ||
value = element.src; | ||
if (shouldReplace(value)) { | ||
const newNode = document.createElement('script'); | ||
newNode.src = replace(value); | ||
element.defer = true; | ||
element.src = ''; | ||
element.before(newNode); | ||
element.remove(); | ||
} | ||
} | ||
|
||
for (element of $('img')) { | ||
value = element.src; | ||
if (shouldReplace(value)) { | ||
// Used to cancel loading. Without this line it will remain pending status. | ||
element.src = ''; | ||
element.src = replace(value); | ||
} | ||
} | ||
|
||
// All elements that have a style attribute | ||
for (element of $('*[style]')) { | ||
value = element.getAttribute('style'); | ||
if (shouldReplace(value)) { | ||
element.setAttribute('style', replace(value)); | ||
} | ||
} | ||
|
||
for (element of $('style')) { | ||
value = element.innerHTML; | ||
if (shouldReplace(value)) { | ||
element.innerHTML = replace(value); | ||
} | ||
} | ||
}; | ||
|
||
const tryReplace = () => { | ||
if (!isRunning && failed && fastNode) { | ||
console.warn(SOURCE + ' is not available. Use ' + fastNode); | ||
isRunning = true; | ||
setTimeout(replaceElementSrc, 0); | ||
// Some need to wait for a while | ||
setTimeout(replaceElementSrc, 20); | ||
// Replace dynamically added elements | ||
setInterval(replaceElementSrc, 500); | ||
} | ||
}; | ||
|
||
const checkAvailable = (url, callback) => { | ||
let timeoutId; | ||
const newNode = document.createElement('link'); | ||
const handleResult = (isSuccess) => { | ||
if (!timeoutId) { | ||
return; | ||
} | ||
|
||
clearTimeout(timeoutId); | ||
timeoutId = 0; | ||
// Used to cancel loading. Without this line it will remain pending status. | ||
if (!isSuccess) newNode.href = 'data:text/css;base64,'; | ||
newNode.remove(); | ||
callback(isSuccess); | ||
}; | ||
|
||
timeoutId = setTimeout(handleResult, TIMEOUT); | ||
|
||
newNode.addEventListener('error', () => handleResult(false)); | ||
newNode.addEventListener('load', () => handleResult(true)); | ||
newNode.rel = 'stylesheet'; | ||
newNode.text = 'text/css'; | ||
newNode.href = url + TEST_PATH + starTime; | ||
document.head.insertAdjacentElement('afterbegin', newNode); | ||
}; | ||
|
||
const cached = (() => { | ||
try { | ||
return Object.assign( | ||
{}, | ||
JSON.parse(localStorage.getItem(STORE_KEY) || '{}') | ||
); | ||
} catch { | ||
return {}; | ||
} | ||
})(); | ||
|
||
const main = () => { | ||
cached.time = starTime; | ||
cached.failed = false; | ||
cached.fastNode = null; | ||
|
||
for (const url of DEST_LIST) { | ||
checkAvailable('https://' + url, (isAvailable) => { | ||
// console.log(url, Date.now() - starTime, Boolean(isAvailable)); | ||
if (!isAvailable && url === SOURCE) { | ||
failed = true; | ||
cached.failed = true; | ||
} | ||
|
||
if (isAvailable && !fastNode) { | ||
fastNode = url; | ||
} | ||
|
||
if (isAvailable && !cached.fastNode) { | ||
cached.fastNode = url; | ||
} | ||
|
||
tryReplace(); | ||
}); | ||
} | ||
|
||
setTimeout(() => { | ||
// If all domains are timeout | ||
if (failed && !fastNode) { | ||
fastNode = DEST_LIST[1]; | ||
tryReplace(); | ||
} | ||
|
||
localStorage.setItem(STORE_KEY, JSON.stringify(cached)); | ||
}, TIMEOUT + 100); | ||
}; | ||
|
||
if ( | ||
cached.time && | ||
starTime - cached.time < 60 * 60 * 1000 && | ||
cached.failed && | ||
cached.fastNode | ||
) { | ||
failed = true; | ||
fastNode = cached.fastNode; | ||
tryReplace(); | ||
setTimeout(main, 1000); | ||
} else if (document.head) { | ||
main(); | ||
} else { | ||
const observer = new MutationObserver(() => { | ||
if (document.head) { | ||
observer.disconnect(); | ||
main(); | ||
} | ||
}); | ||
const observerOptions = { | ||
childList: true, | ||
subtree: true | ||
}; | ||
observer.observe(document, observerOptions); | ||
} | ||
})(document); |