Use URL.parse with polyfill

This commit is contained in:
Lim Chee Aun 2024-06-14 08:34:50 +08:00
parent febd04dd54
commit 1f8a8f8928
18 changed files with 62 additions and 52 deletions

View file

@ -7,6 +7,7 @@
"^[^.].*.css$", "^[^.].*.css$",
"index.css$", "index.css$",
".css$", ".css$",
"./polyfills",
"<THIRD_PARTY_MODULES>", "<THIRD_PARTY_MODULES>",
"/assets/", "/assets/",
"^../", "^../",

View file

@ -231,7 +231,7 @@ function AccountInfo({
const accountInstance = useMemo(() => { const accountInstance = useMemo(() => {
if (!url) return null; if (!url) return null;
const domain = punycode.toUnicode(new URL(url).hostname); const domain = punycode.toUnicode(URL.parse(url).hostname);
return domain; return domain;
}, [url]); }, [url]);
@ -1655,7 +1655,7 @@ function lightenRGB([r, g, b]) {
function niceAccountURL(url) { function niceAccountURL(url) {
if (!url) return; if (!url) return;
const urlObj = new URL(url); const urlObj = URL.parse(url);
const { host, pathname } = urlObj; const { host, pathname } = urlObj;
const path = pathname.replace(/\/$/, '').replace(/^\//, ''); const path = pathname.replace(/\/$/, '').replace(/^\//, '');
return ( return (

View file

@ -58,7 +58,7 @@ function AccountSheet({ account, instance: propInstance, onClose }) {
if (result.accounts.length) { if (result.accounts.length) {
return result.accounts[0]; return result.accounts[0];
} else if (/https?:\/\/[^/]+\/@/.test(account)) { } else if (/https?:\/\/[^/]+\/@/.test(account)) {
const accountURL = new URL(account); const accountURL = URL.parse(account);
const { hostname, pathname } = accountURL; const { hostname, pathname } = accountURL;
const acct = const acct =
pathname.replace(/^\//, '').replace(/\/$/, '') + pathname.replace(/^\//, '').replace(/\/$/, '') +

View file

@ -3292,11 +3292,11 @@ function GIFPickerModal({ onClose = () => {}, onSelect = () => {} }) {
width = (width / height) * 100; width = (width / height) * 100;
height = 100; height = 100;
} }
const urlObj = new URL(url); const urlObj = URL.parse(url);
const strippedURL = urlObj.origin + urlObj.pathname; const strippedURL = urlObj.origin + urlObj.pathname;
let strippedWebP; let strippedWebP;
if (webp) { if (webp) {
const webpObj = new URL(webp); const webpObj = URL.parse(webp);
strippedWebP = webpObj.origin + webpObj.pathname; strippedWebP = webpObj.origin + webpObj.pathname;
} }
return ( return (
@ -3306,7 +3306,7 @@ function GIFPickerModal({ onClose = () => {}, onSelect = () => {} }) {
onClick={() => { onClick={() => {
const { mp4, url } = original; const { mp4, url } = original;
const theURL = mp4 || url; const theURL = mp4 || url;
const urlObj = new URL(theURL); const urlObj = URL.parse(theURL);
const strippedURL = urlObj.origin + urlObj.pathname; const strippedURL = urlObj.origin + urlObj.pathname;
onClose(); onClose();
onSelect({ onSelect({

View file

@ -22,15 +22,13 @@ const Link = forwardRef((props, ref) => {
// Handle encodeURIComponent of searchParams values // Handle encodeURIComponent of searchParams values
if (!!hash && hash !== '/' && hash.includes('?')) { if (!!hash && hash !== '/' && hash.includes('?')) {
try { const parsedHash = URL.parse(hash, location.origin); // Fake base URL
const parsedHash = new URL(hash, location.origin); // Fake base URL if (parsedHash?.searchParams?.size) {
if (parsedHash.searchParams.size) {
const searchParamsStr = Array.from(parsedHash.searchParams.entries()) const searchParamsStr = Array.from(parsedHash.searchParams.entries())
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&'); .join('&');
hash = parsedHash.pathname + '?' + searchParamsStr; hash = parsedHash.pathname + '?' + searchParamsStr;
} }
} catch (e) {}
} }
const isActive = hash === to || decodeURIComponent(hash) === to; const isActive = hash === to || decodeURIComponent(hash) === to;

View file

@ -674,12 +674,8 @@ function Media({
} }
function getURLObj(url) { function getURLObj(url) {
try {
// Fake base URL if url doesn't have https:// prefix // Fake base URL if url doesn't have https:// prefix
return new URL(url, location.origin); return URL.parse(url, location.origin);
} catch (e) {
return null;
}
} }
export default Media; export default Media;

View file

@ -2535,7 +2535,9 @@ function Card({ card, selfReferential, instance }) {
if (hasText && (image || (type === 'photo' && blurhash))) { if (hasText && (image || (type === 'photo' && blurhash))) {
const domain = punycode.toUnicode( const domain = punycode.toUnicode(
new URL(url).hostname.replace(/^www\./, '').replace(/\/$/, ''), URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
); );
let blurhashImage; let blurhashImage;
const rgbAverageColor = const rgbAverageColor =
@ -2662,7 +2664,7 @@ function Card({ card, selfReferential, instance }) {
} }
if (hasText && !image) { if (hasText && !image) {
const domain = punycode.toUnicode( const domain = punycode.toUnicode(
new URL(url).hostname.replace(/^www\./, ''), URL.parse(url).hostname.replace(/^www\./, ''),
); );
return ( return (
<a <a
@ -2863,7 +2865,7 @@ function generateHTMLCode(post, instance, level = 0) {
const mediaURL = previewMediaURL || sourceMediaURL; const mediaURL = previewMediaURL || sourceMediaURL;
const sourceMediaURLObj = sourceMediaURL const sourceMediaURLObj = sourceMediaURL
? new URL(sourceMediaURL) ? URL.parse(sourceMediaURL)
: null; : null;
const isVideoMaybe = const isVideoMaybe =
type === 'unknown' && type === 'unknown' &&
@ -3188,7 +3190,7 @@ function StatusButton({
function nicePostURL(url) { function nicePostURL(url) {
if (!url) return; if (!url) return;
const urlObj = new URL(url); const urlObj = URL.parse(url);
const { host, pathname } = urlObj; const { host, pathname } = urlObj;
const path = pathname.replace(/\/$/, ''); const path = pathname.replace(/\/$/, '');
// split only first slash // split only first slash

View file

@ -2,6 +2,8 @@ import './index.css';
import './app.css'; import './app.css';
import './polyfills';
import { render } from 'preact'; import { render } from 'preact';
import { useEffect, useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';

View file

@ -2,6 +2,8 @@ import './index.css';
import './cloak-mode.css'; import './cloak-mode.css';
import './polyfills';
// Polyfill needed for Firefox < 122 // Polyfill needed for Firefox < 122
// https://bugzilla.mozilla.org/show_bug.cgi?id=1423593 // https://bugzilla.mozilla.org/show_bug.cgi?id=1423593
// import '@formatjs/intl-segmenter/polyfill'; // import '@formatjs/intl-segmenter/polyfill';
@ -14,19 +16,6 @@ if (import.meta.env.DEV) {
import('preact/debug'); import('preact/debug');
} }
// AbortSignal.timeout polyfill
// Temporary fix from https://github.com/mo/abortcontroller-polyfill/issues/73#issuecomment-1541180943
// Incorrect implementation, but should be good enough for now
if ('AbortSignal' in window) {
AbortSignal.timeout =
AbortSignal.timeout ||
((duration) => {
const controller = new AbortController();
setTimeout(() => controller.abort(), duration);
return controller.signal;
});
}
render( render(
<HashRouter> <HashRouter>
<App /> <App />

View file

@ -467,7 +467,7 @@ function AccountStatuses() {
const accountInstance = useMemo(() => { const accountInstance = useMemo(() => {
if (!account?.url) return null; if (!account?.url) return null;
const domain = new URL(account.url).hostname; const domain = URL.parse(account.url).hostname;
return domain; return domain;
}, [account]); }, [account]);
const sameInstance = instance === accountInstance; const sameInstance = instance === accountInstance;

View file

@ -1111,8 +1111,8 @@ function Catchup() {
publishedAt, publishedAt,
} = card; } = card;
const domain = punycode.toUnicode( const domain = punycode.toUnicode(
new URL(url).hostname URL.parse(url)
.replace(/^www\./, '') .hostname.replace(/^www\./, '')
.replace(/\/$/, ''), .replace(/\/$/, ''),
); );
let accentColor; let accentColor;

View file

@ -569,7 +569,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
if (!heroStatus) return; if (!heroStatus) return;
const { url } = heroStatus; const { url } = heroStatus;
if (!url) return; if (!url) return;
return new URL(url).hostname; return URL.parse(url).hostname;
}, [heroStatus]); }, [heroStatus]);
const postSameInstance = useMemo(() => { const postSameInstance = useMemo(() => {
if (!postInstance) return; if (!postInstance) return;

View file

@ -178,7 +178,9 @@ function Trending({ columnMode, ...props }) {
width, width,
} = link; } = link;
const domain = punycode.toUnicode( const domain = punycode.toUnicode(
new URL(url).hostname.replace(/^www\./, '').replace(/\/$/, ''), URL.parse(url)
.hostname.replace(/^www\./, '')
.replace(/\/$/, ''),
); );
let accentColor; let accentColor;
if (blurhash) { if (blurhash) {

24
src/polyfills.js Normal file
View file

@ -0,0 +1,24 @@
// AbortSignal.timeout polyfill
// Temporary fix from https://github.com/mo/abortcontroller-polyfill/issues/73#issuecomment-1541180943
// Incorrect implementation, but should be good enough for now
if ('AbortSignal' in window) {
AbortSignal.timeout =
AbortSignal.timeout ||
((duration) => {
const controller = new AbortController();
setTimeout(() => controller.abort(), duration);
return controller.signal;
});
}
// URL.parse() polyfill
if ('URL' in window && typeof URL.parse !== 'function') {
URL.parse = function (url, base) {
if (!url) return null;
try {
return base ? new URL(url, base) : new URL(url);
} catch (e) {
return null;
}
};
}

View file

@ -11,7 +11,7 @@ const statusPostRegexes = [
export function getInstanceStatusObject(url) { export function getInstanceStatusObject(url) {
// Regex /:username/:id, where username = @username or @username@domain, id = anything // Regex /:username/:id, where username = @username or @username@domain, id = anything
const { hostname, pathname } = new URL(url); const { hostname, pathname } = URL.parse(url);
// const [, username, domain, id] = pathname.match(statusRegex) || []; // const [, username, domain, id] = pathname.match(statusRegex) || [];
for (const regex of statusPostRegexes) { for (const regex of statusPostRegexes) {
const [, id] = pathname.match(regex) || []; const [, id] = pathname.match(regex) || [];

View file

@ -1,6 +1,6 @@
export default function isMastodonLinkMaybe(url) { export default function isMastodonLinkMaybe(url) {
try { try {
const { pathname, hash } = new URL(url); const { pathname, hash } = URL.parse(url);
return ( return (
/^\/.*\/\d+$/i.test(pathname) || /^\/.*\/\d+$/i.test(pathname) ||
/^\/(@[^/]+|users\/[^/]+)\/(statuses|posts)\/\w+\/?$/i.test(pathname) || // GoToSocial, Takahe /^\/(@[^/]+|users\/[^/]+)\/(statuses|posts)\/\w+\/?$/i.test(pathname) || // GoToSocial, Takahe

View file

@ -1,5 +1,5 @@
export default function openCompose(opts) { export default function openCompose(opts) {
const url = new URL('/compose/', window.location); const url = URL.parse('/compose/', window.location);
const { width: screenWidth, height: screenHeight } = window.screen; const { width: screenWidth, height: screenHeight } = window.screen;
const left = Math.max(0, (screenWidth - 600) / 2); const left = Math.max(0, (screenWidth - 600) / 2);
const top = Math.max(0, (screenHeight - 450) / 2); const top = Math.max(0, (screenHeight - 450) / 2);

View file

@ -59,12 +59,8 @@ function _unfurlMastodonLink(instance, url) {
theURL = `https://${finalURL}`; theURL = `https://${finalURL}`;
} }
let urlObj; const urlObj = URL.parse(theURL);
try { if (!urlObj) return;
urlObj = new URL(theURL);
} catch (e) {
return;
}
const domain = urlObj.hostname; const domain = urlObj.hostname;
const path = urlObj.pathname; const path = urlObj.pathname;
// Regex /:username/:id, where username = @username or @username@domain, id = post ID // Regex /:username/:id, where username = @username or @username@domain, id = post ID