nicolium: do some linting but don't enable new rules for now

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-02-26 13:36:33 +01:00
parent 4eafef4294
commit fb1f445c07
92 changed files with 196 additions and 225 deletions

View File

@@ -132,7 +132,7 @@ const otpVerify =
(code: string, mfa_token: string) => (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const app = state.auth.app;
const baseUrl = parseBaseURL(state.me) || BuildConfig.BACKEND_URL;
const baseUrl = parseBaseURL(state.me || undefined) || BuildConfig.BACKEND_URL;
const client = new PlApiClient(baseUrl);
return client.oauth
.mfaChallenge({

View File

@@ -11,7 +11,7 @@ import type { Me } from '@/types/pl-fe';
// Taken from https://www.npmjs.com/package/web-push
const urlBase64ToUint8Array = (base64String: string) => {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const base64 = (base64String + padding).replaceAll('-', '+').replaceAll('_', '/');
return decodeBase64(base64);
};

View File

@@ -106,7 +106,7 @@ const dequeueTimeline =
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timelineId));
// @ts-ignore
// @ts-expect-error
expandFunc();
} else if (timelineId === 'home') {
dispatch(clearTimeline(timelineId));
@@ -166,7 +166,7 @@ const deduplicateStatuses = (statuses: Array<BaseStatus>) => {
reblogged.accounts.push(status.account);
reblogged.id += ':' + status.id;
} else if (
!deduplicatedStatuses.find(
!deduplicatedStatuses.some(
(deduplicatedStatus) => deduplicatedStatus.reblog?.id === status.id,
)
) {

View File

@@ -15,7 +15,7 @@ const env = compileTime(() => {
};
const sanitizeBasename = (path: string | undefined = ''): string =>
`/${path.replace(/^\/+|\/+$/g, '')}`;
`/${path.replaceAll(/^\/+|\/+$/g, '')}`;
return {
NODE_ENV: NODE_ENV ?? 'development',

View File

@@ -15,11 +15,10 @@ interface IEmoji {
const Emoji: React.FC<IEmoji> = ({ emoji, emojiMap, hovered }) => {
const { autoPlayGif, systemEmojiFont } = useSettings();
// @ts-ignore
if (unicodeMapping[emoji]) {
if (systemEmojiFont) return <>{emoji}</>;
// @ts-ignore
// @ts-expect-error
const { unified, shortCode } = unicodeMapping[emoji];
const title = shortCode ? `:${shortCode}:` : '';

View File

@@ -40,9 +40,7 @@ const Reaction: React.FC<IReaction> = ({ announcementId, reaction, emojiMap, sty
let shortCode = reaction.name;
// @ts-ignore
if (unicodeMapping[shortCode]) {
// @ts-ignore
shortCode = unicodeMapping[shortCode].shortcode;
}

View File

@@ -21,7 +21,7 @@ const ReactionsBar: React.FC<IReactionsBar> = ({ announcementId, reactions, emoj
const { addReaction } = useAnnouncements();
const handleEmojiPick = (data: Emoji) => {
addReaction({ announcementId, name: (data as NativeEmoji).native.replace(/:/g, '') });
addReaction({ announcementId, name: (data as NativeEmoji).native.replaceAll(':', '') });
};
const visibleReactions = reactions.filter((x) => x.count > 0);

View File

@@ -116,7 +116,7 @@ const DropdownMenuItem = ({ index, item, onClick, autoFocus, onSetTab }: IDropdo
if (itemRef.current && (autoFocus ? firstItem : item?.active)) {
itemRef.current.focus({ preventScroll: true });
}
}, [itemRef.current, index]);
}, [index]);
if (item === null) {
return <hr />;

View File

@@ -128,7 +128,7 @@ const DropdownMenuContent: React.FC<IDropdownMenuContent> = ({
e.stopPropagation();
}
},
[ref.current],
[],
);
const handleDocumentClick = useMemo(
@@ -138,7 +138,7 @@ const DropdownMenuContent: React.FC<IDropdownMenuContent> = ({
event.stopPropagation();
}
},
[ref.current],
[],
);
useEffect(() => {
@@ -153,7 +153,7 @@ const DropdownMenuContent: React.FC<IDropdownMenuContent> = ({
document.removeEventListener('touchend', handleDocumentClick);
document.removeEventListener('keydown', handleKeyDown);
};
}, [ref.current]);
}, []);
const handleExitSubmenu: React.EventHandler<any> = (event) => {
event.stopPropagation();

View File

@@ -35,7 +35,7 @@ const ExtendedVideoPlayer: React.FC<IExtendedVideoPlayer> = ({
return () => {
video.current?.removeEventListener('loadeddata', handleLoadedData);
};
}, [video.current]);
}, []);
const handleClick: React.MouseEventHandler<HTMLVideoElement> = (e) => {
e.stopPropagation();

View File

@@ -64,7 +64,7 @@ const ListItem: React.FC<IListItem> = ({
: childDescribedBy;
return React.cloneElement(child, {
// @ts-ignore
// @ts-expect-error
id: domId,
'aria-labelledby': ariaLabelledBy,
'aria-describedby': ariaDescribedBy,

View File

@@ -367,7 +367,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
setWidth(offsetWidth);
}
}, [node.current]);
}, []);
const handleClick = (index: number) => {
onOpenMedia(media, index);
@@ -655,7 +655,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
if (compact) {
return {
style: {},
itemsDimensions: [...new Array(size)].map(() => ({
itemsDimensions: new Array(size).fill('').map(() => ({
w: 'auto',
h: 'auto',
top: 'auto',

View File

@@ -29,7 +29,7 @@ const GREENTEXT_CLASS = 'dark:text-accent-green text-lime-600';
const checkSuspiciousUrl = (url: string): boolean => {
try {
const { host } = new URL(url);
return /^verify\.form/.test(host);
return host.startsWith('verify.form');
} catch (e) {
return false;
}
@@ -213,16 +213,16 @@ function parseContent(
const options: HTMLReactParserOptions = {
replace(domNode) {
if (!(domNode instanceof Element)) {
// @ts-ignore
// @ts-expect-error
domNode.preGreentext =
// @ts-ignore
// @ts-expect-error
(!domNode.prev || domNode.prev.preGreentext) && !domNode.data.trim().length;
// @ts-ignore
// @ts-expect-error
const data = domNode.prev?.preGreentext ? domNode.data.trim() : domNode.data;
// @ts-ignore
// @ts-expect-error
if (greentext && (data.startsWith('>') || domNode.prev?.greentext)) {
// @ts-ignore
// @ts-expect-error
domNode.greentext = true;
return <span className={GREENTEXT_CLASS}>{transformText(domNode.data)}</span>;
}
@@ -239,24 +239,24 @@ function parseContent(
}
if (domNode.attribs.class?.split(' ').includes('h-card')) {
// @ts-ignore
// @ts-expect-error
domNode.preGreentext = !domNode.prev || domNode.prev.preGreentext;
}
// @ts-ignore
// @ts-expect-error
if (domNode.name !== 'br' && domNode.prev?.greentext) {
domNode.attribs.class = `${domNode.attribs.class || ''} ${GREENTEXT_CLASS}`;
// @ts-ignore
// @ts-expect-error
domNode.greentext = true;
}
if (domNode.name === 'a') {
const classes = domNode.attribs.class?.split(' ');
// @ts-ignore
// @ts-expect-error
if (domNode.prev?.greentext) {
classes.push(GREENTEXT_CLASS);
// @ts-ignore
// @ts-expect-error
domNode.greentext = true;
}

View File

@@ -26,12 +26,12 @@ const safeParseFloat = (str: unknown): number | null => {
const validTime = (t: string | boolean | null | undefined) => {
if (t === null || t === undefined) return null;
if (typeof t === 'boolean') return null;
return t.match(/^-?[0-9.]+s$/) ? t : null;
return /^-?[0-9.]+s$/.test(t) ? t : null;
};
const validColor = (c: unknown): string | null => {
if (typeof c !== 'string') return null;
return c.match(/^[0-9a-f]{3,6}$/i) ? c : null;
return /^[0-9a-f]{3,6}$/i.test(c) ? c : null;
};
interface IParsedMfm {
@@ -61,7 +61,7 @@ const ParsedMfm: React.FC<IParsedMfm> = React.memo(({ text, emojis, mentions, sp
.map((token): React.JSX.Element | string | (React.JSX.Element | string)[] => {
switch (token.type) {
case 'text': {
let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
let text = token.props.text.replaceAll(/(\r\n|\n|\r)/g, '\n');
if (speakAsCat) text = nyaize(text);

View File

@@ -191,7 +191,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
}}
href={href}
title={trimmedTitle}
rel='noopener'
rel='noopener noreferrer'
target='_blank'
dir={direction}
>
@@ -274,7 +274,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
}}
href={href}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
className='text-gray-700 hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100'
title={intl.formatMessage(messages.externalLink)}
>
@@ -320,7 +320,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
href={href}
className={className}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
ref={setRef}
onClick={(e) => {
e.stopPropagation();
@@ -370,7 +370,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
{linkBody}
</Link>
) : (
<a href={author.url} target='_blank' rel='noopener'>
<a href={author.url} target='_blank' rel='noopener noreferrer'>
{linkBody}
</a>
)}
@@ -396,7 +396,7 @@ const trim = (text: string, len: number): string => {
return text;
}
return text.substring(0, cut) + (text.length > len ? '…' : '');
return text.slice(0, cut) + (text.length > len ? '…' : '');
};
export { PreviewCard as default };

View File

@@ -42,7 +42,7 @@ const SafeEmbed: React.FC<ISafeEmbed> = ({ className, sandbox, title, html }) =>
return () => {
iframe.current?.contentWindow?.removeEventListener('message', handleMessage);
};
}, [iframe.current, html]);
}, [html]);
return (
<iframe ref={iframe} className={className} sandbox={sandbox} height={height} title={title} />

View File

@@ -15,7 +15,7 @@ interface IScrobble {
const Scrobble: React.FC<IScrobble> = ({ scrobble }) => {
const textRef = useRef<HTMLParagraphElement>(null);
const isRecent = new Date().getTime() - new Date(scrobble.created_at).getTime() <= 60 * 60 * 1000;
const isRecent = Date.now() - new Date(scrobble.created_at).getTime() <= 60 * 60 * 1000;
const song = scrobble.artist ? (
<FormattedMessage
@@ -35,7 +35,7 @@ const Scrobble: React.FC<IScrobble> = ({ scrobble }) => {
textRef.current &&
textRef.current.parentElement &&
textRef.current.clientWidth > textRef.current.parentElement.clientWidth,
[textRef.current],
[],
);
if (!isRecent) return null;

View File

@@ -77,9 +77,7 @@ const StatusReaction: React.FC<IStatusReaction> = ({
let shortCode = reaction.name;
// @ts-ignore
if (unicodeMapping[shortCode]?.shortcode) {
// @ts-ignore
shortCode = unicodeMapping[shortCode].shortcode;
}

View File

@@ -42,7 +42,7 @@ const TrendingLink: React.FC<ITrendingLink> = ({ trendingLink }) => {
className='flex cursor-pointer gap-4 overflow-hidden rounded-lg border border-solid border-gray-200 p-4 text-sm text-gray-800 no-underline hover:bg-gray-100 hover:no-underline dark:border-gray-800 dark:text-gray-200 dark:hover:bg-primary-800/30'
href={trendingLink.url}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
>
{media}
<Stack space={2} className='flex-1 overflow-hidden'>

View File

@@ -126,7 +126,7 @@ const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, IButton>(
ref={ref as React.ForwardedRef<HTMLAnchorElement>}
href={href}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
>
{buttonChildren}
</a>

View File

@@ -28,12 +28,12 @@ const FormGroup: React.FC<IFormGroup> = (props) => {
if (React.isValidElement(inputChildren[0])) {
firstChild = React.cloneElement(
inputChildren[0],
// @ts-ignore
// @ts-expect-error
{ id: formFieldId },
);
}
// @ts-ignore
// @ts-expect-error
const isCheckboxFormGroup = firstChild?.type === Checkbox;
if (isCheckboxFormGroup) {

View File

@@ -79,11 +79,11 @@ const HStack = forwardRef<HTMLDivElement, IHStack>((props, ref) => {
className={clsx(
'flex',
{
// @ts-ignore
// @ts-expect-error
[alignItemsOptions[alignItems]]: typeof alignItems !== 'undefined',
// @ts-ignore
// @ts-expect-error
[justifyContentOptions[justifyContent]]: typeof justifyContent !== 'undefined',
// @ts-ignore
// @ts-expect-error
[spaces[space]]: typeof space !== 'undefined',
grow: grow,
'flex-wrap': wrap,

View File

@@ -64,11 +64,11 @@ const Stack = React.forwardRef<HTMLDivElement, IStack>(
className={clsx(
'flex flex-col',
{
// @ts-ignore
// @ts-expect-error
[spaces[space]]: typeof space !== 'undefined',
// @ts-ignore
// @ts-expect-error
[alignItemsOptions[alignItems]]: typeof alignItems !== 'undefined',
// @ts-ignore
// @ts-expect-error
[justifyContentOptions[justifyContent]]: typeof justifyContent !== 'undefined',
grow: grow,
},

View File

@@ -32,21 +32,17 @@ const AnimatedTabs: React.FC<IAnimatedTabs> = ({ children, ...rest }) => {
const ref = React.useRef<any>(null);
const rect = useRect(ref);
// @ts-ignore
// @ts-expect-error
const top: number = (activeRect && activeRect.bottom) - (rect && rect.top);
// @ts-ignore
// @ts-expect-error
const width: number = activeRect && activeRect.width - HORIZONTAL_PADDING * 2;
// @ts-ignore
// @ts-expect-error
const left: number = (activeRect && activeRect.left) - (rect && rect.left) + HORIZONTAL_PADDING;
return (
// @ts-ignore
// @ts-expect-error
<AnimatedContext.Provider value={setActiveRect}>
<ReachTabs
{...rest}
// @ts-ignore
ref={ref}
>
<ReachTabs {...rest} ref={ref}>
<div className='absolute h-[3px] w-full bg-primary-200 dark:bg-gray-800' style={{ top }} />
<div
className={clsx('absolute h-[3px] bg-primary-500 transition-all duration-200', {
@@ -89,15 +85,12 @@ const AnimatedTab: React.FC<IAnimatedTab> = ({ index, ...props }) => {
// callup to set styles whenever we're active
React.useLayoutEffect(() => {
if (isSelected) {
// @ts-ignore
// @ts-expect-error
setActiveRect(rect);
}
}, [isSelected, rect, setActiveRect]);
return (
// @ts-ignore
<ReachTab ref={ref} {...props} />
);
return <ReachTab ref={ref} {...props} />;
};
/** Structure to represent a tab. */
@@ -147,7 +140,7 @@ const Tabs = ({ items, activeItem }: ITabs) => {
key={name}
as='button'
role='button'
// @ts-ignore
// @ts-expect-error
title={title}
index={idx}
>

View File

@@ -53,7 +53,7 @@ const TagInput: React.FC<ITagInput> = ({ tags, onChange, placeholder }) => {
wrap
>
{tags.map((tag, i) => (
<div className='mb-2'>
<div key={tag} className='mb-2'>
<Tag tag={tag} onDelete={handleTagDelete} />
</div>
))}

View File

@@ -308,7 +308,7 @@ const Audio: React.FC<IAudio> = (props) => {
const _initAudioContext = () => {
if (audio.current) {
// @ts-ignore
// @ts-expect-error
// eslint-disable-next-line compat/compat
const AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext();
@@ -420,20 +420,20 @@ const Audio: React.FC<IAudio> = (props) => {
if (player.current) {
_setDimensions();
}
}, [player.current]);
}, []);
useEffect(() => {
if (audio.current) {
setVolume(audio.current.volume);
setMuted(audio.current.muted);
}
}, [audio.current]);
}, []);
useEffect(() => {
if (canvas.current && visualizer.current) {
visualizer.current.setCanvas(canvas.current);
}
}, [canvas.current, visualizer.current]);
}, []);
useEffect(() => {
window.addEventListener('scroll', handleScroll);

View File

@@ -114,7 +114,7 @@ class Visualizer {
if (i < 20) {
let scale = delta / (200 * scaleCoefficient);
scale = scale < 1 ? 1 : scale;
scale = Math.max(1, scale);
allScales.push(scale);
}
});

View File

@@ -29,7 +29,8 @@ const messages = defineMessages({
const BIG_EMOJI_LIMIT = 3;
const parsePendingContent = (content: string) => escape(content).replace(/(?:\r\n|\r|\n)/g, '<br>');
const parsePendingContent = (content: string) =>
escape(content).replaceAll(/(?:\r\n|\r|\n)/g, '<br>');
const parseContent = (chatMessage: ChatMessageEntity) => {
const content = chatMessage.content || '';

View File

@@ -35,7 +35,7 @@ const ChatsPage: React.FC = () => {
useLayoutEffect(() => {
calculateHeight();
}, [containerRef.current]);
}, []);
useEffect(() => {
window.addEventListener('resize', calculateHeight);

View File

@@ -126,7 +126,7 @@ const getLanguageDropdown =
const search = (value: string) => {
if (value === '') {
return [...languages].sort((a, b) => {
return languages.toSorted((a, b) => {
// Push current selection to the top of the list
if (a[0] in textMap) {
@@ -168,13 +168,13 @@ const getLanguageDropdown =
useEffect(() => {
if (!language) input.current?.focus();
}, [input.current]);
}, []);
useEffect(() => {
if (node.current) {
(node.current?.querySelector('button[aria-selected=true]') as HTMLButtonElement)?.focus();
}
}, [node.current]);
}, []);
const isSearching = searchValue !== '';

View File

@@ -201,13 +201,7 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({ composeId, compact }) =>
const valueOption = useMemo(
() =>
[
options,
options
.filter((option) => option.items)
.map((option) => option.items)
.flat(),
]
[options, options.filter((option) => option.items).flatMap((option) => option.items)]
.flat()
.find((item) => item!.value === value),
[value, lists, circles],

View File

@@ -12,7 +12,7 @@ const isCurrentOrFutureDate = (date: Date) =>
date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
const isFiveMinutesFromNow = (selectedDate: Date) => {
const fiveMinutesFromNow = new Date(new Date().getTime() + 1000 * 60 * 5);
const fiveMinutesFromNow = new Date(Date.now() + 1000 * 60 * 5);
return fiveMinutesFromNow.getTime() < selectedDate.getTime();
};
@@ -50,7 +50,7 @@ const ScheduleForm: React.FC<IScheduleForm> = ({ composeId }) => {
const isValidTime = useCallback(
(date: Date) =>
isFiveMinutesFromNow(date) ||
(features.scheduledStatusesBackwards && new Date().getTime() > date.getTime()),
(features.scheduledStatusesBackwards && Date.now() > date.getTime()),
[features.scheduledStatusesBackwards],
);

View File

@@ -57,7 +57,6 @@ const UploadButton: React.FC<IUploadButton> = ({ onSelectFile }) => {
if (e.target.files?.length) {
setDisabled(true);
// @ts-ignore
dispatch(
uploadFile(
e.target.files.item(0) as File,

View File

@@ -105,7 +105,7 @@ const StatePlugin: React.FC<IStatePlugin> = ({ composeId, isWysiwyg }) => {
for (const tag of hashtagNodes) {
const text = tag.getTextContent();
if (text.length > 10 && text.toLowerCase() === text && !text.match(/[0-9]/)) {
if (text.length > 10 && text.toLowerCase() === text && !/[0-9]/.test(text)) {
actions.updateCompose(composeId, (draft) => {
draft.hashtagCasingSuggestion = text;
});

View File

@@ -24,7 +24,7 @@ const IMAGE_TRANSFORMER: TextMatchTransformer = {
if ($isImageNode(node)) {
const src = node.getSrc();
const alt = node.getAltText();
return `![${alt.replace(/([[\]])/g, '\\$1')}](${src})`;
return `![${alt.replaceAll(/([[\]])/g, '\\$1')}](${src})`;
}
return null;
},

View File

@@ -5,6 +5,6 @@ const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx';
const countableText = (inputText: string) =>
inputText
.replace(urlRegex, urlPlaceholder)
.replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/gi, '$1@$3');
.replaceAll(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/gi, '$1@$3');
export { countableText };

View File

@@ -15,7 +15,7 @@ const regexSupplant = (regex: string | RegExp, flags = '') => {
regex = regex.source;
}
return new RegExp(
regex.replace(/#\{(\w+)\}/g, (match, name) => {
regex.replaceAll(/#\{(\w+)\}/g, (match, name) => {
let newRegex = regexen[name] || '';
if (typeof newRegex !== 'string') {
newRegex = newRegex.source;
@@ -27,11 +27,11 @@ const regexSupplant = (regex: string | RegExp, flags = '') => {
};
const stringSupplant = (str: string, values: { [x: string]: any }) =>
str.replace(/#\{(\w+)\}/g, (match, name) => values[name] ?? '');
str.replaceAll(/#\{(\w+)\}/g, (match, name) => values[name] ?? '');
const urlRegex = (() => {
regexen.spaces_group =
/\x09-\x0D\x20\x85\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/; // eslint-disable-line no-control-regex
/\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/; // eslint-disable-line no-control-regex
regexen.invalid_chars_group = /\uFFFE\uFEFF\uFFFF\u202A-\u202E/;
// eslint-disable-next-line no-useless-escape
regexen.punct = /!'#%&@,:;<=>_~{}\$\?\^\*\+\-\.\(\)\[\]\|\/\\/;
@@ -165,7 +165,7 @@ const urlRegex = (() => {
);
regexen.validPortNumber = /[0-9]+/;
regexen.pd =
/\u002d\u058a\u05be\u1400\u1806\u2010-\u2015\u2e17\u2e1a\u2e3a\u2e40\u301c\u3030\u30a0\ufe31\ufe58\ufe63\uff0d/;
/\u002D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E40\u301C\u3030\u30A0\uFE31\uFE58\uFE63\uFF0D/;
regexen.validGeneralUrlPathChars = regexSupplant(/[^#{spaces_group}()?]/i);
// Allow URL paths to contain up to two nested levels of balanced parens
// 1. Used in Wikipedia URLs like /Primer_(film)

View File

@@ -21,7 +21,7 @@ const buildStatus = (account: Account, draftStatus: DraftStatus) => {
const status = v.parse(statusSchema, {
id: 'draft',
account,
content: draftStatus.text.replace(
content: draftStatus.text.replaceAll(
new RegExp('\n', 'g'),
'<br>',
) /* eslint-disable-line no-control-regex */,

View File

@@ -10,7 +10,7 @@ import('./data')
.then((data) => {
emojis = data.emojis;
const sortedEmojis = Object.entries(emojis).sort((a, b) => a[0].localeCompare(b[0]));
const sortedEmojis = Object.entries(emojis).toSorted((a, b) => a[0].localeCompare(b[0]));
for (const [key, emoji] of sortedEmojis) {
index.add('n' + key, `${emoji.id} ${emoji.name} ${emoji.keywords.join(' ')}`);
}
@@ -29,7 +29,7 @@ interface searchOptions {
}
const addCustomToPool = (customEmojis: any[]) => {
// @ts-ignore
// @ts-expect-error
for (const key in index.register) {
if (key[0] === 'c') {
index.remove(key); // remove old custom emojis

View File

@@ -279,7 +279,7 @@ const getNotificationStatus = (
'quoted_update',
].includes(n.type)
)
// @ts-ignore
// @ts-expect-error
return n.status;
return null;
};

View File

@@ -21,7 +21,7 @@ const IconPickerMenu: React.FC<IIconPickerMenu> = ({ icons, onPick }) => {
useEffect(() => {
const firstButton = containerNode.current?.querySelector('button') as HTMLButtonElement;
firstButton?.focus();
}, [containerNode.current]);
}, []);
const handleClick = (icon: string) => {
onPick(icon);

View File

@@ -413,7 +413,7 @@ const Preferences = () => {
<a
className='underline'
href='https://hosted.weblate.org/projects/pl-fe/pl-fe/'
rel='noopener'
rel='noopener noreferrer'
target='_blank'
>
{children}

View File

@@ -15,7 +15,7 @@ const buildStatus = (account: Account, scheduledStatus: ScheduledStatus) => {
const status = v.parse(statusSchema, {
account,
content: scheduledStatus.params.text?.replace(
content: scheduledStatus.params.text?.replaceAll(
new RegExp('\n', 'g'),
'<br>',
) /* eslint-disable-line no-control-regex */,

View File

@@ -137,7 +137,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
<a
href={actualStatus.url}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
className='hover:underline'
>
<FormattedDate
@@ -157,7 +157,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
<a
href={actualStatus.application.website ?? '#'}
target='_blank'
rel='noopener'
rel='noopener noreferrer'
className='hover:underline'
title={intl.formatMessage(messages.applicationName, {
name: actualStatus.application.name,
@@ -171,11 +171,9 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
{actualStatus.edited_at && (
<>
<span className='⁂-separator' />
<div
<button
className='inline hover:underline'
onClick={handleOpenCompareHistoryModal}
role='button'
tabIndex={0}
>
<FormattedMessage
id='status.edited'
@@ -190,7 +188,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
}),
}}
/>
</div>
</button>
</>
)}
</HStack>

View File

@@ -60,7 +60,6 @@ const ThreadStatus: React.FC<IThreadStatus> = (props): React.JSX.Element => {
>
{renderConnector()}
{isLoaded ? (
// @ts-ignore FIXME
<StatusContainer {...props} showGroup={false} />
) : (
<PlaceholderStatus variant='default' />

View File

@@ -29,7 +29,7 @@ const Palette: React.FC<IPalette> = ({
resetKey,
allowTintChange = true,
}) => {
const tints = Object.keys(palette).sort(compareId);
const tints = Object.keys(palette).toSorted(compareId);
const [hue, setHue] = useState(0);
const lastHue = usePrevious(hue);

View File

@@ -217,7 +217,7 @@ function useHotkeys<T extends HTMLElement>(handlers: HandlerMap) {
});
// Sort all matches by priority
matchCandidates.sort((a, b) => b.priority - a.priority);
matchCandidates.toSorted((a, b) => b.priority - a.priority);
const bestMatchingHandler = matchCandidates.at(0)?.handler;
if (bestMatchingHandler) {

View File

@@ -25,7 +25,7 @@ const LinkFooter: React.FC = (): React.JSX.Element => {
values={{
code_name: sourceCode.displayName,
code_link: (
<a href={sourceCode.url} rel='noopener' target='_blank'>
<a href={sourceCode.url} rel='noopener noreferrer' target='_blank'>
{sourceCode.repository}
</a>
),

View File

@@ -25,10 +25,10 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
const children = useMemo(() => {
if (isLoading || !account) return <Spinner />;
const publicVisibilities = ['public', 'unlisted'];
const publicVisibilities = new Set(['public', 'unlisted']);
const publicAttachments = attachments
.filter((attachment) => publicVisibilities.includes(attachment.visibility))
.filter((attachment) => publicVisibilities.has(attachment.visibility))
.slice(0, 9);
if (publicAttachments.length) {

View File

@@ -21,7 +21,7 @@ import PollPreview from './poll-preview';
import type { Status as StatusEntity } from '@/normalizers/status';
const shouldHaveCard = (pendingStatus: StatusEntity) =>
Boolean(pendingStatus.content.match(/https?:\/\/\S*/));
Boolean(/https?:\/\/\S*/.test(pendingStatus.content));
interface IPendingStatus {
className?: string;

View File

@@ -44,8 +44,8 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
dispatch(logOut());
};
const handleSwitchAccount = (account: AccountEntity) => () => {
dispatch(switchAccount(account.id));
const handleSwitchAccount = (otherAccount: AccountEntity) => () => {
dispatch(switchAccount(otherAccount.id));
};
const renderAccount = (account: AccountEntity) => (

View File

@@ -5,7 +5,7 @@ const isFullscreen = (): boolean =>
Boolean(
// eslint-disable-next-line compat/compat
document.fullscreenElement ??
// @ts-ignore
// @ts-expect-error
document.webkitFullscreenElement,
);
@@ -13,7 +13,7 @@ const exitFullscreen = (): void => {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if ('webkitExitFullscreen' in document) {
// @ts-ignore
// @ts-expect-error
document.webkitExitFullscreen();
}
};
@@ -22,7 +22,7 @@ const requestFullscreen = (el: Element): void => {
if (el.requestFullscreen) {
el.requestFullscreen();
} else if ('webkitRequestFullscreen' in el) {
// @ts-ignore
// @ts-expect-error
el.webkitRequestFullscreen();
}
};

View File

@@ -18,7 +18,7 @@ const buildMentions = (pendingStatus: PendingStatus) => {
const buildPoll = (pendingStatus: PendingStatus) => {
if (pendingStatus.poll?.options) {
return create(pendingStatus.poll, (draft) => {
// @ts-ignore
// @ts-expect-error
draft.options = draft.options.map((title) => ({ title }));
});
} else {
@@ -36,10 +36,7 @@ const buildStatus = (
const status = {
account,
content: pendingStatus.status.replace(
new RegExp('\n', 'g'),
'<br>',
) /* eslint-disable-line no-control-regex */,
content: pendingStatus.status.replaceAll('\n', '<br>'),
id: `末pending-${idempotencyKey}`,
in_reply_to_account_id: state.statuses[inReplyToId ?? '']?.account_id || null,
in_reply_to_id: inReplyToId,

View File

@@ -166,14 +166,14 @@ const Video: React.FC<IVideo> = ({
useLayoutEffect(() => {
setDimensions();
}, [player.current]);
}, []);
useEffect(() => {
if (video.current) {
setVolume(video.current.volume);
setMuted(video.current.muted);
}
}, [video.current]);
}, []);
const handleClickRoot: React.MouseEventHandler = (e) => {
e.stopPropagation();

View File

@@ -2,7 +2,7 @@ import messages from '@/messages';
import { useSettings } from '@/stores/settings';
/** Locales which should be presented in right-to-left. */
const RTL_LOCALES = ['ar', 'ckb', 'fa', 'he'];
const RTL_LOCALES = new Set(['ar', 'ckb', 'fa', 'he']);
/** Get valid locale from settings. */
const useLocale = (fallback = 'en') => {
@@ -15,6 +15,6 @@ const useLocale = (fallback = 'en') => {
: fallback;
};
const useLocaleDirection = (locale = 'en') => (RTL_LOCALES.includes(locale) ? 'rtl' : 'ltr');
const useLocaleDirection = (locale = 'en') => (RTL_LOCALES.has(locale) ? 'rtl' : 'ltr');
export { useLocale, useLocaleDirection };

View File

@@ -56,14 +56,14 @@ const normalizeColors = (
const normalizedColors = toTailwind({
brandColor,
accentColor,
// @ts-ignore
// @ts-expect-error
colors,
});
return {
// @ts-ignore
// @ts-expect-error
'gradient-start': normalizedColors.primary?.['500'],
// @ts-ignore
// @ts-expect-error
'gradient-end': normalizedColors.accent?.['500'],
...normalizedColors,
} as typeof normalizedColors;

View File

@@ -19,7 +19,7 @@ import { useCompose, useComposeActions } from '@/stores/compose';
import type { BaseModalProps } from '@/features/ui/components/modal-root';
import type { CreateStatusParams, InteractionPolicy } from 'pl-api';
const MANAGABLE_VISIBILITIES = ['public', 'unlisted', 'private'];
const MANAGABLE_VISIBILITIES = new Set(['public', 'unlisted', 'private']);
interface ComposeInteractionPolicyModalProps {
composeId: string;
@@ -35,7 +35,7 @@ const ComposeInteractionPolicyModal: React.FC<
const { interactionPolicies: initial } = useInteractionPolicies();
const compose = useCompose(composeId);
const canManageInteractionPolicies = MANAGABLE_VISIBILITIES.includes(compose.visibility);
const canManageInteractionPolicies = MANAGABLE_VISIBILITIES.has(compose.visibility);
useEffect(() => {
if (!canManageInteractionPolicies) {

View File

@@ -85,15 +85,13 @@ const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({
reactionUrl: reactionRecord.url ?? undefined,
}));
} else {
return reactions
.map(({ account_ids, name, url }) =>
account_ids.map((account) => ({
id: account,
reaction: name,
reactionUrl: url ?? undefined,
})),
)
.flat();
return reactions.flatMap(({ account_ids, name, url }) =>
account_ids.map((account) => ({
id: account,
reaction: name,
reactionUrl: url ?? undefined,
})),
);
}
}, [reactions, reaction]);

View File

@@ -73,7 +73,7 @@ const ReasonStep: React.FC<IReasonStep> = ({ comment, setComment, ruleIds, setRu
setNearBottom(true);
}
}
}, [rules, rulesListRef.current]);
}, [rules]);
return (
<Stack space={4}>

View File

@@ -11,9 +11,9 @@ const normalizeNotification = (notification: BaseNotification): NotificationGrou
page_max_id: notification.id,
latest_page_notification_at: notification.created_at,
sample_account_ids: [notification.account.id],
// @ts-ignore
// @ts-expect-error
status_id: notification.status?.id,
// @ts-ignore
// @ts-expect-error
target_id: notification.target?.id,
});

View File

@@ -30,7 +30,7 @@ const getOrderedLists = (lists: Array<Pick<ListEntity, 'title'>>) => {
return Object.values(lists)
.filter((item): item is ListEntity => !!item)
.sort((a, b) => a.title.localeCompare(b.title));
.toSorted((a, b) => a.title.localeCompare(b.title));
};
const NewListForm: React.FC = () => {

View File

@@ -43,10 +43,10 @@ const Dashboard: React.FC = () => {
const [today] = useState<string>(new Date().toISOString().slice(0, 10));
const [monthAgo] = useState<string>(
new Date(new Date().getTime() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10),
new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10),
);
const [sixMonthsAgo] = useState<string>(
new Date(new Date().getTime() - 30 * 6 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10),
new Date(Date.now() - 30 * 6 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10),
);
if (!account) return null;

View File

@@ -177,7 +177,7 @@ const FrontendConfigEditor: React.FC = () => {
if (path[0] === 'cryptoAddresses') {
draft.cryptoAddresses = values;
} else {
// @ts-ignore
// @ts-expect-error
draft[path[0]][path[1]] = values;
}
});
@@ -191,7 +191,7 @@ const FrontendConfigEditor: React.FC = () => {
if (path[0] === 'cryptoAddresses') {
draft.cryptoAddresses.push(v.parse(cryptoAddressSchema, {}));
} else {
// @ts-ignore
// @ts-expect-error
draft[path[0]][path[1]].push(v.parse(schema, {}));
}
});
@@ -203,7 +203,7 @@ const FrontendConfigEditor: React.FC = () => {
if (path[0] === 'cryptoAddresses') {
draft.cryptoAddresses = draft.cryptoAddresses.filter((_, index) => index !== i);
} else {
// @ts-ignore
// @ts-expect-error
draft[path[0]][path[1]] = draft[path[0]][path[1]].filter((_, index) => index !== i);
}
});

View File

@@ -79,8 +79,8 @@ const ReportStatuses: React.FC<IReportStatuses> = ({ statusIds }) => {
)}
<ReactSwipeableViews animateHeight index={index} onChangeIndex={handleChangeIndex}>
{statusIds.map((statusId) => (
<div className='w-full'>
<StatusContainer key={statusId} id={statusId} />
<div className='w-full' key={statusId}>
<StatusContainer id={statusId} />
</div>
))}
</ReactSwipeableViews>

View File

@@ -67,6 +67,7 @@ const Reports: React.FC = () => {
value={[
account && (
<FormattedMessage
key='account'
id='column.admin.reports.filter_message.account'
defaultMessage='from @{acct}'
values={{
@@ -76,6 +77,7 @@ const Reports: React.FC = () => {
),
targetAccount && (
<FormattedMessage
key='targetAccount'
id='column.admin.reports.filter_message.target_account'
defaultMessage='targeting @{acct}'
values={{

View File

@@ -69,7 +69,7 @@ const AuthToken: React.FC<IAuthToken> = ({ token, isCurrent }) => {
<HStack space={1} alignItems='center'>
{token.app_name}
{token.app_website && (
<a href={token.app_website} target='_blank' rel='noopener'>
<a href={token.app_website} target='_blank' rel='noopener noreferrer'>
<Icon
src={require('@phosphor-icons/core/regular/arrow-square-out.svg')}
className='inline size-4 text-inherit'

View File

@@ -296,15 +296,15 @@ const InteractionRequestsPage = () => {
const handleMoveUp = (id: string) => {
const elementIndex = data.findIndex((item) => item !== null && item.id === id) - 1;
_selectChild(elementIndex);
selectChild(elementIndex);
};
const handleMoveDown = (id: string) => {
const elementIndex = data.findIndex((item) => item !== null && item.id === id) + 1;
_selectChild(elementIndex);
selectChild(elementIndex);
};
const _selectChild = (index: number) => {
const selectChild = (index: number) => {
const selector = `[data-index="${index}"] .focusable`;
const element = document.querySelector<HTMLDivElement>(selector);

View File

@@ -39,7 +39,7 @@ const About: React.FC<IAbout> = ({ slug }) => {
setLocale(defaultLocale);
}}
>
{/* @ts-ignore */}
{/* @ts-expect-error */}
{languages[defaultLocale] ?? defaultLocale}
</a>
</li>
@@ -51,7 +51,7 @@ const About: React.FC<IAbout> = ({ slug }) => {
setLocale(locale);
}}
>
{/* @ts-ignore */}
{/* @ts-expect-error */}
{languages[locale] ?? locale}
</a>
</li>

View File

@@ -21,7 +21,7 @@ const useFamiliarFollowers = (accountId: string) => {
for (const account of accounts) {
queryClient.setQueryData(['accounts', account.id], account);
}
accounts.map(({ id }) => id);
return accounts.map(({ id }) => id);
}),
enabled: isLoggedIn && features.familiarFollowers,
});

View File

@@ -38,12 +38,10 @@ const makeUseFollowRequests = <T>(select: (data: InfiniteData<PaginatedResponse<
'isLoggedIn',
);
const useFollowRequests = makeUseFollowRequests((data) =>
data.pages.map((page) => page.items).flat(),
);
const useFollowRequests = makeUseFollowRequests((data) => data.pages.flatMap((page) => page.items));
const useFollowRequestsCount = makeUseFollowRequests(
(data) => data.pages.map((page) => page.items).flat().length,
(data) => data.pages.flatMap((page) => page.items).length,
);
const useOutgoingFollowRequests = makePaginatedResponseQuery(

View File

@@ -70,7 +70,7 @@ const usePendingUsersCount = () => {
return useInfiniteQuery({
...pendingUsersQuery,
select: (data) =>
(data.pages.at(-1)?.total ?? data.pages.map((page) => page.items).flat().length) || 0,
(data.pages.at(-1)?.total ?? data.pages.flatMap((page) => page.items).length) || 0,
enabled: !!(account?.is_admin ?? account?.is_moderator),
});
};

View File

@@ -39,7 +39,7 @@ const usePendingReportsCount = () => {
return useInfiniteQuery({
...pendingReportsQuery,
select: (data) =>
(data.pages.at(-1)?.total ?? data.pages.map((page) => page.items).flat().length) || 0,
(data.pages.at(-1)?.total ?? data.pages.flatMap((page) => page.items).length) || 0,
enabled: !!instance.fetched && !!(account?.is_admin ?? account?.is_moderator),
});
};

View File

@@ -108,7 +108,7 @@ const useAnnouncements = () => {
});
return {
data: data ? [...data].sort(compareAnnouncements) : undefined,
data: data?.toSorted(compareAnnouncements),
...result,
addReaction,
removeReaction,

View File

@@ -190,7 +190,7 @@ const useCreateChatMessage = (chatId: string) => {
// Snapshot the previous value
const prevContent = variables.content;
const prevChatMessages = queryClient.getQueryData(['chats', 'messages', variables.chatId]);
const pendingId = String(Number(new Date()));
const pendingId = String(Date.now());
// Optimistically update to the new value
queryClient.setQueryData(ChatKeys.chatMessages(variables.chatId), (prevResult: any) => {

View File

@@ -14,7 +14,7 @@ const scheduledStatusesQueryOptions = makePaginatedResponseQueryOptions(
const scheduledStatusesCountQueryOptions = infiniteQueryOptions({
...scheduledStatusesQueryOptions,
select: (data) => data.pages.map((page) => page.items).flat().length,
select: (data) => data.pages.flatMap((page) => page.items).length,
});
const cancelScheduledStatusMutationOptions = (scheduledStatusId: string) =>

View File

@@ -29,7 +29,7 @@ const minifyInteractionRequestsList = (
): PaginatedResponse<MinifiedInteractionRequest> => {
dispatch(
importEntities({
statuses: items.map((item) => [item.status, item.reply]).flat(),
statuses: items.flatMap((item) => [item.status, item.reply]),
}),
);
@@ -74,11 +74,11 @@ const useInteractionRequests = <T>(
const useFlatInteractionRequests = () =>
useInteractionRequests((data: InfiniteData<PaginatedResponse<MinifiedInteractionRequest>>) =>
data.pages.map((page) => page.items).flat(),
data.pages.flatMap((page) => page.items),
);
const useInteractionRequestsCount = () =>
useInteractionRequests((data) => data.pages.map(({ items }) => items).flat().length);
useInteractionRequests((data) => data.pages.flatMap(({ items }) => items).length);
const useAuthorizeInteractionRequestMutation = (requestId: string) => {
const client = useClient();

View File

@@ -33,10 +33,10 @@ const getFilters = (state: Pick<RootState, 'filters'>, query: FilterContext) =>
state.filters.filter(
(filter) =>
(!query?.contextType || filter.context.includes(toServerSideType(query.contextType))) &&
(filter.expires_at === null || Date.parse(filter.expires_at) > new Date().getTime()),
(filter.expires_at === null || Date.parse(filter.expires_at) > Date.now()),
);
const escapeRegExp = (string: string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
const escapeRegExp = (string: string) => string.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
const regexFromFilters = (filters: Array<Filter>) => {
if (filters.length === 0) return null;

View File

@@ -154,9 +154,9 @@ const formatMessage = (messageId: string, locale: string, values = {}): string =
const htmlToPlainText = (html: string): string =>
unescape(
html
.replace(/<br\s*\/?>/g, '\n')
.replace(/<\/p><[^>]*>/g, '\n\n')
.replace(/<[^>]*>/g, ''),
.replaceAll(/<br\s*\/?>/g, '\n')
.replaceAll(/<\/p><[^>]*>/g, '\n\n')
.replaceAll(/<[^>]*>/g, ''),
);
/** ServiceWorker `push` event callback. */
@@ -218,7 +218,7 @@ const handlePush = (event: PushEvent) => {
body,
icon,
tag: notification_id,
timestamp: Number(new Date()),
timestamp: Date.now(),
data: { access_token, preferred_locale, url: '/notifications' },
}),
),

View File

@@ -6,7 +6,7 @@ const filtered = compileTime(() => {
const filenames = fs.readdirSync(path.resolve(__dirname, '../locales'));
filenames.forEach((filename) => {
if (!filename.match(/\.json$/) || filename.match(/defaultMessages|whitelist/)) return;
if (!filename.endsWith('.json') || filename.match(/defaultMessages|whitelist/)) return;
const content = fs.readFileSync(path.resolve(__dirname, `../locales/${filename}`), 'utf-8');
const full = JSON.parse(content) as Record<string, string>;

View File

@@ -723,10 +723,10 @@ const useSubmitCompose = (composeId: string) => {
if (!preview) {
const scheduledAt = compose.scheduledAt;
if (scheduledAt) {
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
const fiveMinutesFromNow = new Date(Date.now() + 300000);
const valid =
scheduledAt.getTime() > fiveMinutesFromNow.getTime() ||
(features.scheduledStatusesBackwards && scheduledAt.getTime() < new Date().getTime());
(features.scheduledStatusesBackwards && scheduledAt.getTime() < Date.now());
if (!valid) {
toast.error(messages.scheduleError);
return;
@@ -760,12 +760,7 @@ const useSubmitCompose = (composeId: string) => {
to = [
...new Set([
...to,
...mentionsMatch.map((mention) =>
mention
.replace(/&#x20;/g, '')
.trim()
.slice(1),
),
...mentionsMatch.map((mention) => mention.replaceAll('&#x20;', '').trim().slice(1)),
]),
];
}
@@ -808,7 +803,7 @@ const useSubmitCompose = (composeId: string) => {
};
if (compose.editedId) {
// @ts-ignore
// @ts-expect-error
params.media_attributes = media.map((item) => {
const focalPoint = (item.type === 'image' || item.type === 'gifv') && item.meta?.focus;
const focus = focalPoint
@@ -860,7 +855,7 @@ const useSubmitCompose = (composeId: string) => {
} catch {}
} else {
if (compose.redacting) {
// @ts-ignore
// @ts-expect-error
params.overwrite = compose.redactingOverwrite;
}

View File

@@ -2,7 +2,8 @@ import { selectAccount, selectOwnAccount } from '@/queries/accounts/selectors';
import type { RootState } from '@/store';
const validId = (id: any) => typeof id === 'string' && id !== 'null' && id !== 'undefined';
const validId = (id?: string | null | false) =>
typeof id === 'string' && id !== 'null' && id !== 'undefined';
const isURL = (url?: string | null) => {
if (typeof url !== 'string') return false;
@@ -14,7 +15,8 @@ const isURL = (url?: string | null) => {
}
};
const parseBaseURL = (url: any) => {
const parseBaseURL = (url?: string) => {
if (typeof url !== 'string') return '';
try {
return new URL(url).origin;
} catch {

View File

@@ -5,8 +5,8 @@ import pkg from '../../package.json';
const code = compileTime(() => {
const { CI_COMMIT_TAG, CI_COMMIT_REF_NAME, CI_COMMIT_SHA } = process.env;
const shortRepoName = (url: string): string => new URL(url).pathname.substring(1);
const trimHash = (hash: string): string => hash.substring(0, 7);
const shortRepoName = (url: string): string => new URL(url).pathname.slice(1);
const trimHash = (hash: string): string => hash.slice(0, 7);
const tryGit = (cmd: string): string | undefined => {
try {

View File

@@ -28,7 +28,7 @@ SOFTWARE.
import type { Rgb, TailwindColorObject } from '@/types/colors';
const hexToRgb = (hex: string): Rgb | null => {
const sanitizedHex = hex.replace(/##/g, '#');
const sanitizedHex = hex.replaceAll('##', '#');
const colorParts = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(sanitizedHex);
if (!colorParts) {
@@ -79,7 +79,7 @@ const darken = (hex: string, intensity: number): string => {
const colors = (baseColor: string): TailwindColorObject => {
const response: TailwindColorObject = {
500: `#${baseColor}`.replace(/##/g, '#'),
500: `#${baseColor}`.replaceAll('##', '#'),
};
const intensityMap: {

View File

@@ -3,9 +3,9 @@
const unescapeHTML = (html: string = ''): string => {
const wrapper = document.createElement('div');
wrapper.innerHTML = html
.replace(/<br\s*\/?>/g, '\n')
.replace(/<\/p><[^>]*>/g, '\n\n')
.replace(/<[^>]*>/g, '');
.replaceAll(/<br\s*\/?>/g, '\n')
.replaceAll(/<\/p><[^>]*>/g, '\n\n')
.replaceAll(/<[^>]*>/g, '');
return wrapper.textContent || '';
};

View File

@@ -7,7 +7,7 @@ const truncateFilename = (url: string, maxLength: number) => {
if (filename.length <= maxLength) return filename;
return [filename.substr(0, maxLength / 2), filename.substr(filename.length - maxLength / 2)].join(
return [filename.slice(0, maxLength / 2), filename.slice(filename.length - maxLength / 2)].join(
'…',
);
};
@@ -16,7 +16,7 @@ const formatBytes = (bytes: number, decimals: number = 2) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const dm = Math.max(0, decimals);
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));

View File

@@ -9,7 +9,7 @@ const ifAfter = (prefix: string, fn: (x: string) => string) => {
const preLen = prefix.length;
const regex = new RegExp(prefix, 'i');
return (x: string, pos: number, string: string) => {
return pos > 0 && string.substring(pos - preLen, pos).match(regex) ? fn(x) : x;
return pos > 0 && string.slice(pos - preLen, pos).match(regex) ? fn(x) : x;
};
};
@@ -20,26 +20,26 @@ const nyaize = (text: string) =>
.replaceAll('ナ', 'ニャ')
.replaceAll('ナ', 'ニャ')
// en-US
.replace(
/a/gi,
.replaceAll(
'a',
ifAfter('n', (x) => (x === 'A' ? 'YA' : 'ya')),
)
.replace(
/ing/gi,
.replaceAll(
'ing',
ifAfter('morn', (x) => (x === 'ING' ? 'YAN' : 'yan')),
)
.replace(
/one/gi,
.replaceAll(
'one',
ifAfter('every', (x) => (x === 'ONE' ? 'NYAN' : 'nyan')),
)
// pl-PL
.replace(
/ł/gi,
.replaceAll(
'ł',
ifAfter('mia', (x) => (x === 'Ł' ? 'U' : 'u')),
)
// ru-RU
.replace(
/а/gi,
.replaceAll(
'а',
ifAfter('н', (x) => (x === 'А' ? 'Я' : 'я')),
)
// ko-KR

View File

@@ -95,7 +95,7 @@ const sortQueryData = <T>(queryKey: QueryKey, comparator: (a: T, b: T) => number
if (prevResult) {
const nextResult = { ...prevResult };
const flattenedQueryData = flattenPages(nextResult);
const sortedQueryData = flattenedQueryData?.sort(comparator);
const sortedQueryData = flattenedQueryData?.toSorted(comparator);
const paginatedPages = paginateQueryData(sortedQueryData);
const newPages = paginatedPages.map((page: T, idx: number) => ({
...prevResult.pages[idx],

View File

@@ -51,7 +51,7 @@ const dropOrientationIfNeeded = (orientation: number) =>
/** Convert the file into a local blob URL. */
const getImageUrl = (inputFile: File) =>
new Promise<string>((resolve, reject) => {
// @ts-ignore: This is a browser capabilities check.
// @ts-expect-error: This is a browser capabilities check.
if (window.URL?.createObjectURL) {
try {
resolve(URL.createObjectURL(inputFile));
@@ -177,7 +177,7 @@ const processImage = (
reject(blob);
return;
}
resolve(new File([blob], name, { type, lastModified: new Date().getTime() }));
resolve(new File([blob], name, { type, lastModified: Date.now() }));
}, type);
});
@@ -214,7 +214,7 @@ const resize = async (
): Promise<File> => {
if (!hasCanvasExtractPermission) return inputFile;
if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
if (!/image.*/.test(inputFile.type) || inputFile.type === 'image/gif') {
return inputFile;
}

View File

@@ -9,7 +9,7 @@ const onlyEmoji = (node: HTMLElement, limit = 1, ignoreMentions = true): boolean
node.querySelectorAll('a.mention').forEach((m) => m.parentNode?.removeChild(m));
}
if (node.textContent?.replace(new RegExp(' ', 'g'), '') !== '') return false;
if (node.textContent?.replaceAll(new RegExp(' ', 'g'), '') !== '') return false;
const emojis = Array.from(node.querySelectorAll('img.emojione'));
if (emojis.length === 0) return false;
if (emojis.length > limit) return false;

View File

@@ -22,17 +22,17 @@ const isRtl = (text: string, confidence = 0.3): boolean => {
}
// Remove http(s), (s)ftp, ws(s), blob and smtp(s) links
text = text.replace(/(?:https?|ftp|sftp|ws|wss|blob|smtp|smtps):\/\/[\S]+/g, '');
text = text.replaceAll(/(?:https?|ftp|sftp|ws|wss|blob|smtp|smtps):\/\/[\S]+/g, '');
// Remove email address links
text = text.replace(/(mailto:)([^\s@]+@[^\s@]+\.[^\s@]+)/g, '');
text = text.replaceAll(/(mailto:)([^\s@]+@[^\s@]+\.[^\s@]+)/g, '');
// Remove phone number links
text = text.replace(/(tel:)([+\d\s()-]+)/g, '');
text = text.replaceAll(/(tel:)([+\d\s()-]+)/g, '');
// Remove mentions
text = text.replace(/(?:^|[^/\w])@([a-z0-9_]+(@[a-z0-9.-]+)?)/gi, '');
text = text.replaceAll(/(?:^|[^/\w])@([a-z0-9_]+(@[a-z0-9.-]+)?)/gi, '');
// Remove hashtags
text = text.replace(/(?:^|[^/\w])#([\S]+)/gi, '');
text = text.replaceAll(/(?:^|[^/\w])#([\S]+)/gi, '');
// Remove all non-word characters
text = text.replace(/\s+/g, '');
text = text.replaceAll(/\s+/g, '');
const matches = text.match(rtlChars);

View File

@@ -8,7 +8,7 @@ const rgbToHsl = (value: Rgb): Hsl => {
const r = value.r / 255;
const g = value.g / 255;
const b = value.b / 255;
const rgbOrdered = [r, g, b].sort();
const rgbOrdered = [r, g, b].toSorted();
const l = ((rgbOrdered[0] + rgbOrdered[2]) / 2) * 100;
let s, h;
if (rgbOrdered[0] === rgbOrdered[2]) {

View File

@@ -160,7 +160,7 @@ const config = defineConfig(() => ({
name: 'mock-api',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (/^\/api\//.test(req.url!)) {
if (req.url?.startsWith('/api')) {
res.statusCode = 404;
res.end('Not Found');
} else {