From 51b83ed19536b06ce3f57b260400ecec2d1dd187 Mon Sep 17 00:00:00 2001 From: Nick Schonning <nschonni@gmail.com> Date: Tue, 9 May 2023 13:02:12 -0400 Subject: [PATCH] Use Prettier for ESLint formatting TypeScript (#23631) --- .eslintrc.js | 29 +-- .prettierignore | 2 - .prettierrc.js | 3 +- app/javascript/mastodon/blurhash.ts | 4 +- app/javascript/mastodon/compare_id.ts | 2 +- .../mastodon/components/animated_number.tsx | 50 +++-- .../mastodon/components/avatar_overlay.tsx | 14 +- .../mastodon/components/blurhash.tsx | 2 +- app/javascript/mastodon/components/check.tsx | 12 +- app/javascript/mastodon/components/gifv.tsx | 26 ++- app/javascript/mastodon/components/icon.tsx | 15 +- .../mastodon/components/icon_button.tsx | 28 +-- .../mastodon/components/icon_with_badge.tsx | 15 +- app/javascript/mastodon/components/image.tsx | 14 +- .../components/not_signed_in_indicator.tsx | 5 +- .../mastodon/components/radio_button.tsx | 8 +- .../components/relative_timestamp.tsx | 189 ++++++++++++------ app/javascript/mastodon/permissions.ts | 6 +- .../mastodon/polyfills/base_polyfills.ts | 2 +- .../mastodon/reducers/missed_updates.ts | 22 +- app/javascript/mastodon/scroll.ts | 37 +++- app/javascript/mastodon/store/index.ts | 14 +- .../mastodon/store/middlewares/errors.ts | 4 +- .../mastodon/store/middlewares/loading_bar.ts | 43 ++-- .../mastodon/store/middlewares/sounds.ts | 13 +- app/javascript/mastodon/utils/filters.ts | 24 +-- app/javascript/mastodon/utils/hashtags.ts | 26 +-- app/javascript/mastodon/utils/numbers.ts | 13 +- app/javascript/mastodon/uuid.ts | 4 +- package.json | 2 + yarn.lock | 24 +++ 31 files changed, 407 insertions(+), 245 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2bbe301f0..9e965791b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { 'plugin:import/recommended', 'plugin:promise/recommended', 'plugin:jsdoc/recommended', + 'plugin:prettier/recommended', ], env: { @@ -62,20 +63,9 @@ module.exports = { }, rules: { - 'brace-style': 'warn', - 'comma-dangle': ['error', 'always-multiline'], - 'comma-spacing': [ - 'warn', - { - before: false, - after: true, - }, - ], - 'comma-style': ['warn', 'last'], 'consistent-return': 'error', 'dot-notation': 'error', eqeqeq: ['error', 'always', { 'null': 'ignore' }], - indent: ['warn', 2], 'jsx-quotes': ['error', 'prefer-single'], 'no-case-declarations': 'off', 'no-catch-shadow': 'error', @@ -95,7 +85,6 @@ module.exports = { { property: 'substr', message: 'Use .slice instead of .substr.' }, ], 'no-self-assign': 'off', - 'no-trailing-spaces': 'warn', 'no-unused-expressions': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': [ @@ -107,29 +96,14 @@ module.exports = { ignoreRestSiblings: true, }, ], - 'object-curly-spacing': ['error', 'always'], - 'padded-blocks': [ - 'error', - { - classes: 'always', - }, - ], - quotes: ['error', 'single'], - semi: 'error', 'valid-typeof': 'error', 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }], 'react/jsx-boolean-value': 'error', - 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], - 'react/jsx-curly-spacing': 'error', 'react/display-name': 'off', 'react/jsx-equals-spacing': 'error', - 'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'], - 'react/jsx-indent': ['error', 2], 'react/jsx-no-bind': 'error', 'react/jsx-no-target-blank': 'off', - 'react/jsx-tag-spacing': 'error', - 'react/jsx-wrap-multilines': 'error', 'react/no-deprecated': 'off', 'react/no-unknown-property': 'off', 'react/self-closing-comp': 'error', @@ -291,6 +265,7 @@ module.exports = { 'plugin:import/typescript', 'plugin:promise/recommended', 'plugin:jsdoc/recommended', + 'plugin:prettier/recommended', ], rules: { diff --git a/.prettierignore b/.prettierignore index 9bdf76911..2ea407533 100644 --- a/.prettierignore +++ b/.prettierignore @@ -70,8 +70,6 @@ app/javascript/styles/mastodon/reset.scss # Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631 *.js *.jsx -*.ts -*.tsx # Ignore HTML till cleaned and included in CI *.html diff --git a/.prettierrc.js b/.prettierrc.js index 1d70813d5..af39b253f 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,3 +1,4 @@ module.exports = { - singleQuote: true + singleQuote: true, + jsxSingleQuote: true } diff --git a/app/javascript/mastodon/blurhash.ts b/app/javascript/mastodon/blurhash.ts index cb1c3b2c8..dadf2b7f2 100644 --- a/app/javascript/mastodon/blurhash.ts +++ b/app/javascript/mastodon/blurhash.ts @@ -98,9 +98,9 @@ export const decode83 = (str: string) => { }; export const intToRGB = (int: number) => ({ - r: Math.max(0, (int >> 16)), + r: Math.max(0, int >> 16), g: Math.max(0, (int >> 8) & 255), - b: Math.max(0, (int & 255)), + b: Math.max(0, int & 255), }); export const getAverageFromBlurhash = (blurhash: string) => { diff --git a/app/javascript/mastodon/compare_id.ts b/app/javascript/mastodon/compare_id.ts index 3ddfb7635..30b057248 100644 --- a/app/javascript/mastodon/compare_id.ts +++ b/app/javascript/mastodon/compare_id.ts @@ -1,4 +1,4 @@ -export function compareId (id1: string, id2: string) { +export function compareId(id1: string, id2: string) { if (id1 === id2) { return 0; } diff --git a/app/javascript/mastodon/components/animated_number.tsx b/app/javascript/mastodon/components/animated_number.tsx index 20ffa1a4d..f6c77d35f 100644 --- a/app/javascript/mastodon/components/animated_number.tsx +++ b/app/javascript/mastodon/components/animated_number.tsx @@ -16,13 +16,10 @@ const obfuscatedCount = (count: number) => { type Props = { value: number; obfuscate?: boolean; -} -export const AnimatedNumber: React.FC<Props> = ({ - value, - obfuscate, -})=> { +}; +export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => { const [previousValue, setPreviousValue] = useState(value); - const [direction, setDirection] = useState<1|-1>(1); + const [direction, setDirection] = useState<1 | -1>(1); if (previousValue !== value) { setPreviousValue(value); @@ -30,24 +27,45 @@ export const AnimatedNumber: React.FC<Props> = ({ } const willEnter = useCallback(() => ({ y: -1 * direction }), [direction]); - const willLeave = useCallback(() => ({ y: spring(1 * direction, { damping: 35, stiffness: 400 }) }), [direction]); + const willLeave = useCallback( + () => ({ y: spring(1 * direction, { damping: 35, stiffness: 400 }) }), + [direction] + ); if (reduceMotion) { - return obfuscate ? <>{obfuscatedCount(value)}</> : <ShortNumber value={value} />; + return obfuscate ? ( + <>{obfuscatedCount(value)}</> + ) : ( + <ShortNumber value={value} /> + ); } - const styles = [{ - key: `${value}`, - data: value, - style: { y: spring(0, { damping: 35, stiffness: 400 }) }, - }]; + const styles = [ + { + key: `${value}`, + data: value, + style: { y: spring(0, { damping: 35, stiffness: 400 }) }, + }, + ]; return ( - <TransitionMotion styles={styles} willEnter={willEnter} willLeave={willLeave}> - {items => ( + <TransitionMotion + styles={styles} + willEnter={willEnter} + willLeave={willLeave} + > + {(items) => ( <span className='animated-number'> {items.map(({ key, data, style }) => ( - <span key={key} style={{ position: (direction * style.y) > 0 ? 'absolute' : 'static', transform: `translateY(${style.y * 100}%)` }}>{obfuscate ? obfuscatedCount(data) : <ShortNumber value={data} />}</span> + <span + key={key} + style={{ + position: direction * style.y > 0 ? 'absolute' : 'static', + transform: `translateY(${style.y * 100}%)`, + }} + > + {obfuscate ? obfuscatedCount(data) : <ShortNumber value={data} />} + </span> ))} </span> )} diff --git a/app/javascript/mastodon/components/avatar_overlay.tsx b/app/javascript/mastodon/components/avatar_overlay.tsx index e8dc88896..1dbd53323 100644 --- a/app/javascript/mastodon/components/avatar_overlay.tsx +++ b/app/javascript/mastodon/components/avatar_overlay.tsx @@ -18,13 +18,19 @@ export const AvatarOverlay: React.FC<Props> = ({ baseSize = 36, overlaySize = 24, }) => { - const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(autoPlayGif); - const accountSrc = hovering ? account?.get('avatar') : account?.get('avatar_static'); - const friendSrc = hovering ? friend?.get('avatar') : friend?.get('avatar_static'); + const { hovering, handleMouseEnter, handleMouseLeave } = + useHovering(autoPlayGif); + const accountSrc = hovering + ? account?.get('avatar') + : account?.get('avatar_static'); + const friendSrc = hovering + ? friend?.get('avatar') + : friend?.get('avatar_static'); return ( <div - className='account__avatar-overlay' style={{ width: size, height: size }} + className='account__avatar-overlay' + style={{ width: size, height: size }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > diff --git a/app/javascript/mastodon/components/blurhash.tsx b/app/javascript/mastodon/components/blurhash.tsx index 181e2183d..700513676 100644 --- a/app/javascript/mastodon/components/blurhash.tsx +++ b/app/javascript/mastodon/components/blurhash.tsx @@ -8,7 +8,7 @@ type Props = { dummy?: boolean; // Whether dummy mode is enabled. If enabled, nothing is rendered and canvas left untouched children?: never; [key: string]: any; -} +}; const Blurhash: React.FC<Props> = ({ hash, width = 32, diff --git a/app/javascript/mastodon/components/check.tsx b/app/javascript/mastodon/components/check.tsx index 57b810a0e..73d65595e 100644 --- a/app/javascript/mastodon/components/check.tsx +++ b/app/javascript/mastodon/components/check.tsx @@ -1,7 +1,15 @@ import React from 'react'; export const Check: React.FC = () => ( - <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'> - <path fillRule='evenodd' d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' clipRule='evenodd' /> + <svg + xmlns='http://www.w3.org/2000/svg' + viewBox='0 0 20 20' + fill='currentColor' + > + <path + fillRule='evenodd' + d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' + clipRule='evenodd' + /> </svg> ); diff --git a/app/javascript/mastodon/components/gifv.tsx b/app/javascript/mastodon/components/gifv.tsx index 5d9f235e1..72914ba74 100644 --- a/app/javascript/mastodon/components/gifv.tsx +++ b/app/javascript/mastodon/components/gifv.tsx @@ -8,7 +8,7 @@ type Props = { width: number; height: number; onClick?: () => void; -} +}; export const GIFV: React.FC<Props> = ({ src, @@ -17,19 +17,23 @@ export const GIFV: React.FC<Props> = ({ width, height, onClick, -})=> { +}) => { const [loading, setLoading] = useState(true); - const handleLoadedData: React.ReactEventHandler<HTMLVideoElement> = useCallback(() => { - setLoading(false); - }, [setLoading]); + const handleLoadedData: React.ReactEventHandler<HTMLVideoElement> = + useCallback(() => { + setLoading(false); + }, [setLoading]); - const handleClick: React.MouseEventHandler = useCallback((e) => { - if (onClick) { - e.stopPropagation(); - onClick(); - } - }, [onClick]); + const handleClick: React.MouseEventHandler = useCallback( + (e) => { + if (onClick) { + e.stopPropagation(); + onClick(); + } + }, + [onClick] + ); return ( <div className='gifv' style={{ position: 'relative' }}> diff --git a/app/javascript/mastodon/components/icon.tsx b/app/javascript/mastodon/components/icon.tsx index f74437b55..4eb948dc7 100644 --- a/app/javascript/mastodon/components/icon.tsx +++ b/app/javascript/mastodon/components/icon.tsx @@ -7,6 +7,15 @@ type Props = { fixedWidth?: boolean; children?: never; [key: string]: any; -} -export const Icon: React.FC<Props> = ({ id, className, fixedWidth, ...other }) => - <i className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} {...other} />; +}; +export const Icon: React.FC<Props> = ({ + id, + className, + fixedWidth, + ...other +}) => ( + <i + className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} + {...other} + /> +); diff --git a/app/javascript/mastodon/components/icon_button.tsx b/app/javascript/mastodon/components/icon_button.tsx index f9db287bb..178641400 100644 --- a/app/javascript/mastodon/components/icon_button.tsx +++ b/app/javascript/mastodon/components/icon_button.tsx @@ -25,13 +25,12 @@ type Props = { obfuscateCount?: boolean; href?: string; ariaHidden: boolean; -} +}; type States = { - activate: boolean, - deactivate: boolean, -} + activate: boolean; + deactivate: boolean; +}; export class IconButton extends React.PureComponent<Props, States> { - static defaultProps = { size: 18, active: false, @@ -47,7 +46,7 @@ export class IconButton extends React.PureComponent<Props, States> { deactivate: false, }; - UNSAFE_componentWillReceiveProps (nextProps: Props) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { if (!nextProps.animate) return; if (this.props.active && !nextProps.active) { @@ -57,7 +56,7 @@ export class IconButton extends React.PureComponent<Props, States> { } } - handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => { + handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => { e.preventDefault(); if (!this.props.disabled && this.props.onClick != null) { @@ -83,7 +82,7 @@ export class IconButton extends React.PureComponent<Props, States> { } }; - render () { + render() { const style = { fontSize: `${this.props.size}px`, width: `${this.props.size * 1.28571429}px`, @@ -109,10 +108,7 @@ export class IconButton extends React.PureComponent<Props, States> { ariaHidden, } = this.props; - const { - activate, - deactivate, - } = this.state; + const { activate, deactivate } = this.state; const classes = classNames(className, 'icon-button', { active, @@ -130,7 +126,12 @@ export class IconButton extends React.PureComponent<Props, States> { let contents = ( <React.Fragment> - <Icon id={icon} fixedWidth aria-hidden='true' /> {typeof counter !== 'undefined' && <span className='icon-button__counter'><AnimatedNumber value={counter} obfuscate={obfuscateCount} /></span>} + <Icon id={icon} fixedWidth aria-hidden='true' />{' '} + {typeof counter !== 'undefined' && ( + <span className='icon-button__counter'> + <AnimatedNumber value={counter} obfuscate={obfuscateCount} /> + </span> + )} </React.Fragment> ); @@ -162,5 +163,4 @@ export class IconButton extends React.PureComponent<Props, States> { </button> ); } - } diff --git a/app/javascript/mastodon/components/icon_with_badge.tsx b/app/javascript/mastodon/components/icon_with_badge.tsx index a4af86ca9..bf86814c0 100644 --- a/app/javascript/mastodon/components/icon_with_badge.tsx +++ b/app/javascript/mastodon/components/icon_with_badge.tsx @@ -1,18 +1,25 @@ import React from 'react'; import { Icon } from './icon'; -const formatNumber = (num: number): number | string => num > 40 ? '40+' : num; +const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num); type Props = { id: string; count: number; issueBadge: boolean; className: string; -} -export const IconWithBadge: React.FC<Props> = ({ id, count, issueBadge, className }) => ( +}; +export const IconWithBadge: React.FC<Props> = ({ + id, + count, + issueBadge, + className, +}) => ( <i className='icon-with-badge'> <Icon id={id} fixedWidth className={className} /> - {count > 0 && <i className='icon-with-badge__badge'>{formatNumber(count)}</i>} + {count > 0 && ( + <i className='icon-with-badge__badge'>{formatNumber(count)}</i> + )} {issueBadge && <i className='icon-with-badge__issue-badge' />} </i> ); diff --git a/app/javascript/mastodon/components/image.tsx b/app/javascript/mastodon/components/image.tsx index b332d4115..490543424 100644 --- a/app/javascript/mastodon/components/image.tsx +++ b/app/javascript/mastodon/components/image.tsx @@ -7,9 +7,14 @@ type Props = { srcSet?: string; blurhash?: string; className?: string; -} +}; -export const Image: React.FC<Props> = ({ src, srcSet, blurhash, className }) => { +export const Image: React.FC<Props> = ({ + src, + srcSet, + blurhash, + className, +}) => { const [loaded, setLoaded] = useState(false); const handleLoad = useCallback(() => { @@ -17,7 +22,10 @@ export const Image: React.FC<Props> = ({ src, srcSet, blurhash, className }) => }, [setLoaded]); return ( - <div className={classNames('image', { loaded }, className)} role='presentation'> + <div + className={classNames('image', { loaded }, className)} + role='presentation' + > {blurhash && <Blurhash hash={blurhash} className='image__preview' />} <img src={src} srcSet={srcSet} alt='' onLoad={handleLoad} /> </div> diff --git a/app/javascript/mastodon/components/not_signed_in_indicator.tsx b/app/javascript/mastodon/components/not_signed_in_indicator.tsx index 3bfee6ae9..53945d6a7 100644 --- a/app/javascript/mastodon/components/not_signed_in_indicator.tsx +++ b/app/javascript/mastodon/components/not_signed_in_indicator.tsx @@ -4,7 +4,10 @@ import { FormattedMessage } from 'react-intl'; export const NotSignedInIndicator: React.FC = () => ( <div className='scrollable scrollable--flex'> <div className='empty-column-indicator'> - <FormattedMessage id='not_signed_in_indicator.not_signed_in' defaultMessage='You need to sign in to access this resource.' /> + <FormattedMessage + id='not_signed_in_indicator.not_signed_in' + defaultMessage='You need to sign in to access this resource.' + /> </div> </div> ); diff --git a/app/javascript/mastodon/components/radio_button.tsx b/app/javascript/mastodon/components/radio_button.tsx index 194b67afe..829f47174 100644 --- a/app/javascript/mastodon/components/radio_button.tsx +++ b/app/javascript/mastodon/components/radio_button.tsx @@ -9,7 +9,13 @@ type Props = { label: React.ReactNode; }; -export const RadioButton: React.FC<Props> = ({ name, value, checked, onChange, label }) => { +export const RadioButton: React.FC<Props> = ({ + name, + value, + checked, + onChange, + label, +}) => { return ( <label className='radio-button'> <input diff --git a/app/javascript/mastodon/components/relative_timestamp.tsx b/app/javascript/mastodon/components/relative_timestamp.tsx index 4f1a76e54..65d9d27cb 100644 --- a/app/javascript/mastodon/components/relative_timestamp.tsx +++ b/app/javascript/mastodon/components/relative_timestamp.tsx @@ -4,20 +4,50 @@ import { injectIntl, defineMessages, InjectedIntl } from 'react-intl'; const messages = defineMessages({ today: { id: 'relative_time.today', defaultMessage: 'today' }, just_now: { id: 'relative_time.just_now', defaultMessage: 'now' }, - just_now_full: { id: 'relative_time.full.just_now', defaultMessage: 'just now' }, + just_now_full: { + id: 'relative_time.full.just_now', + defaultMessage: 'just now', + }, seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' }, - seconds_full: { id: 'relative_time.full.seconds', defaultMessage: '{number, plural, one {# second} other {# seconds}} ago' }, + seconds_full: { + id: 'relative_time.full.seconds', + defaultMessage: '{number, plural, one {# second} other {# seconds}} ago', + }, minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' }, - minutes_full: { id: 'relative_time.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}} ago' }, + minutes_full: { + id: 'relative_time.full.minutes', + defaultMessage: '{number, plural, one {# minute} other {# minutes}} ago', + }, hours: { id: 'relative_time.hours', defaultMessage: '{number}h' }, - hours_full: { id: 'relative_time.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}} ago' }, + hours_full: { + id: 'relative_time.full.hours', + defaultMessage: '{number, plural, one {# hour} other {# hours}} ago', + }, days: { id: 'relative_time.days', defaultMessage: '{number}d' }, - days_full: { id: 'relative_time.full.days', defaultMessage: '{number, plural, one {# day} other {# days}} ago' }, - moments_remaining: { id: 'time_remaining.moments', defaultMessage: 'Moments remaining' }, - seconds_remaining: { id: 'time_remaining.seconds', defaultMessage: '{number, plural, one {# second} other {# seconds}} left' }, - minutes_remaining: { id: 'time_remaining.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}} left' }, - hours_remaining: { id: 'time_remaining.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}} left' }, - days_remaining: { id: 'time_remaining.days', defaultMessage: '{number, plural, one {# day} other {# days}} left' }, + days_full: { + id: 'relative_time.full.days', + defaultMessage: '{number, plural, one {# day} other {# days}} ago', + }, + moments_remaining: { + id: 'time_remaining.moments', + defaultMessage: 'Moments remaining', + }, + seconds_remaining: { + id: 'time_remaining.seconds', + defaultMessage: '{number, plural, one {# second} other {# seconds}} left', + }, + minutes_remaining: { + id: 'time_remaining.minutes', + defaultMessage: '{number, plural, one {# minute} other {# minutes}} left', + }, + hours_remaining: { + id: 'time_remaining.hours', + defaultMessage: '{number, plural, one {# hour} other {# hours}} left', + }, + days_remaining: { + id: 'time_remaining.days', + defaultMessage: '{number, plural, one {# day} other {# days}} left', + }, }); const dateFormatOptions = { @@ -36,8 +66,8 @@ const shortDateFormatOptions = { const SECOND = 1000; const MINUTE = 1000 * 60; -const HOUR = 1000 * 60 * 60; -const DAY = 1000 * 60 * 60 * 24; +const HOUR = 1000 * 60 * 60; +const DAY = 1000 * 60 * 60 * 24; const MAX_DELAY = 2147483647; @@ -57,20 +87,27 @@ const selectUnits = (delta: number) => { const getUnitDelay = (units: string) => { switch (units) { - case 'second': - return SECOND; - case 'minute': - return MINUTE; - case 'hour': - return HOUR; - case 'day': - return DAY; - default: - return MAX_DELAY; + case 'second': + return SECOND; + case 'minute': + return MINUTE; + case 'hour': + return HOUR; + case 'day': + return DAY; + default: + return MAX_DELAY; } }; -export const timeAgoString = (intl: InjectedIntl, date: Date, now: number, year: number, timeGiven: boolean, short?: boolean) => { +export const timeAgoString = ( + intl: InjectedIntl, + date: Date, + now: number, + year: number, + timeGiven: boolean, + short?: boolean +) => { const delta = now - date.getTime(); let relativeTime; @@ -78,27 +115,49 @@ export const timeAgoString = (intl: InjectedIntl, date: Date, now: number, year: if (delta < DAY && !timeGiven) { relativeTime = intl.formatMessage(messages.today); } else if (delta < 10 * SECOND) { - relativeTime = intl.formatMessage(short ? messages.just_now : messages.just_now_full); + relativeTime = intl.formatMessage( + short ? messages.just_now : messages.just_now_full + ); } else if (delta < 7 * DAY) { if (delta < MINUTE) { - relativeTime = intl.formatMessage(short ? messages.seconds : messages.seconds_full, { number: Math.floor(delta / SECOND) }); + relativeTime = intl.formatMessage( + short ? messages.seconds : messages.seconds_full, + { number: Math.floor(delta / SECOND) } + ); } else if (delta < HOUR) { - relativeTime = intl.formatMessage(short ? messages.minutes : messages.minutes_full, { number: Math.floor(delta / MINUTE) }); + relativeTime = intl.formatMessage( + short ? messages.minutes : messages.minutes_full, + { number: Math.floor(delta / MINUTE) } + ); } else if (delta < DAY) { - relativeTime = intl.formatMessage(short ? messages.hours : messages.hours_full, { number: Math.floor(delta / HOUR) }); + relativeTime = intl.formatMessage( + short ? messages.hours : messages.hours_full, + { number: Math.floor(delta / HOUR) } + ); } else { - relativeTime = intl.formatMessage(short ? messages.days : messages.days_full, { number: Math.floor(delta / DAY) }); + relativeTime = intl.formatMessage( + short ? messages.days : messages.days_full, + { number: Math.floor(delta / DAY) } + ); } } else if (date.getFullYear() === year) { relativeTime = intl.formatDate(date, shortDateFormatOptions); } else { - relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' }); + relativeTime = intl.formatDate(date, { + ...shortDateFormatOptions, + year: 'numeric', + }); } return relativeTime; }; -const timeRemainingString = (intl: InjectedIntl, date: Date, now: number, timeGiven = true) => { +const timeRemainingString = ( + intl: InjectedIntl, + date: Date, + now: number, + timeGiven = true +) => { const delta = date.getTime() - now; let relativeTime; @@ -108,13 +167,21 @@ const timeRemainingString = (intl: InjectedIntl, date: Date, now: number, timeGi } else if (delta < 10 * SECOND) { relativeTime = intl.formatMessage(messages.moments_remaining); } else if (delta < MINUTE) { - relativeTime = intl.formatMessage(messages.seconds_remaining, { number: Math.floor(delta / SECOND) }); + relativeTime = intl.formatMessage(messages.seconds_remaining, { + number: Math.floor(delta / SECOND), + }); } else if (delta < HOUR) { - relativeTime = intl.formatMessage(messages.minutes_remaining, { number: Math.floor(delta / MINUTE) }); + relativeTime = intl.formatMessage(messages.minutes_remaining, { + number: Math.floor(delta / MINUTE), + }); } else if (delta < DAY) { - relativeTime = intl.formatMessage(messages.hours_remaining, { number: Math.floor(delta / HOUR) }); + relativeTime = intl.formatMessage(messages.hours_remaining, { + number: Math.floor(delta / HOUR), + }); } else { - relativeTime = intl.formatMessage(messages.days_remaining, { number: Math.floor(delta / DAY) }); + relativeTime = intl.formatMessage(messages.days_remaining, { + number: Math.floor(delta / DAY), + }); } return relativeTime; @@ -126,78 +193,86 @@ type Props = { year: number; futureDate?: boolean; short?: boolean; -} +}; type States = { now: number; -} +}; class RelativeTimestamp extends React.Component<Props, States> { - state = { now: this.props.intl.now(), }; static defaultProps = { - year: (new Date()).getFullYear(), + year: new Date().getFullYear(), short: true, }; _timer: number | undefined; - shouldComponentUpdate (nextProps: Props, nextState: States) { + shouldComponentUpdate(nextProps: Props, nextState: States) { // As of right now the locale doesn't change without a new page load, // but we might as well check in case that ever changes. - return this.props.timestamp !== nextProps.timestamp || + return ( + this.props.timestamp !== nextProps.timestamp || this.props.intl.locale !== nextProps.intl.locale || - this.state.now !== nextState.now; + this.state.now !== nextState.now + ); } - UNSAFE_componentWillReceiveProps (nextProps: Props) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { if (this.props.timestamp !== nextProps.timestamp) { this.setState({ now: this.props.intl.now() }); } } - componentDidMount () { + componentDidMount() { this._scheduleNextUpdate(this.props, this.state); } - UNSAFE_componentWillUpdate (nextProps: Props, nextState: States) { + UNSAFE_componentWillUpdate(nextProps: Props, nextState: States) { this._scheduleNextUpdate(nextProps, nextState); } - componentWillUnmount () { + componentWillUnmount() { window.clearTimeout(this._timer); } - _scheduleNextUpdate (props: Props, state: States) { + _scheduleNextUpdate(props: Props, state: States) { window.clearTimeout(this._timer); - const { timestamp } = props; - const delta = (new Date(timestamp)).getTime() - state.now; - const unitDelay = getUnitDelay(selectUnits(delta)); - const unitRemainder = Math.abs(delta % unitDelay); + const { timestamp } = props; + const delta = new Date(timestamp).getTime() - state.now; + const unitDelay = getUnitDelay(selectUnits(delta)); + const unitRemainder = Math.abs(delta % unitDelay); const updateInterval = 1000 * 10; - const delay = delta < 0 ? Math.max(updateInterval, unitDelay - unitRemainder) : Math.max(updateInterval, unitRemainder); + const delay = + delta < 0 + ? Math.max(updateInterval, unitDelay - unitRemainder) + : Math.max(updateInterval, unitRemainder); this._timer = window.setTimeout(() => { this.setState({ now: this.props.intl.now() }); }, delay); } - render () { + render() { const { timestamp, intl, year, futureDate, short } = this.props; - const timeGiven = timestamp.includes('T'); - const date = new Date(timestamp); - const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven, short); + const timeGiven = timestamp.includes('T'); + const date = new Date(timestamp); + const relativeTime = futureDate + ? timeRemainingString(intl, date, this.state.now, timeGiven) + : timeAgoString(intl, date, this.state.now, year, timeGiven, short); return ( - <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}> + <time + dateTime={timestamp} + title={intl.formatDate(date, dateFormatOptions)} + > {relativeTime} </time> ); } - } const RelativeTimestampWithIntl = injectIntl(RelativeTimestamp); diff --git a/app/javascript/mastodon/permissions.ts b/app/javascript/mastodon/permissions.ts index 9ea149e5f..b583535c0 100644 --- a/app/javascript/mastodon/permissions.ts +++ b/app/javascript/mastodon/permissions.ts @@ -1,4 +1,4 @@ -export const PERMISSION_INVITE_USERS = 0x0000000000010000; -export const PERMISSION_MANAGE_USERS = 0x0000000000000400; +export const PERMISSION_INVITE_USERS = 0x0000000000010000; +export const PERMISSION_MANAGE_USERS = 0x0000000000000400; export const PERMISSION_MANAGE_FEDERATION = 0x0000000000000020; -export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010; +export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010; diff --git a/app/javascript/mastodon/polyfills/base_polyfills.ts b/app/javascript/mastodon/polyfills/base_polyfills.ts index 2e583f580..64211c11e 100644 --- a/app/javascript/mastodon/polyfills/base_polyfills.ts +++ b/app/javascript/mastodon/polyfills/base_polyfills.ts @@ -10,7 +10,7 @@ if (!HTMLCanvasElement.prototype.toBlob) { const BASE64_MARKER = ';base64,'; Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { - value(callback: BlobCallback, type = 'image/png', quality: any) { + value(callback: BlobCallback, type = 'image/png', quality: any) { const dataURL = this.toDataURL(type, quality); let data; diff --git a/app/javascript/mastodon/reducers/missed_updates.ts b/app/javascript/mastodon/reducers/missed_updates.ts index 628fed2a9..9c1a5cbd2 100644 --- a/app/javascript/mastodon/reducers/missed_updates.ts +++ b/app/javascript/mastodon/reducers/missed_updates.ts @@ -14,18 +14,18 @@ const initialState = Record<MissedUpdatesState>({ export function missedUpdatesReducer( state = initialState, - action: Action<string>, + action: Action<string> ) { switch (action.type) { - case focusApp.type: - return state.set('focused', true).set('unread', 0); - case unfocusApp.type: - return state.set('focused', false); - case NOTIFICATIONS_UPDATE: - return state.get('focused') - ? state - : state.update('unread', (x) => x + 1); - default: - return state; + case focusApp.type: + return state.set('focused', true).set('unread', 0); + case unfocusApp.type: + return state.set('focused', false); + case NOTIFICATIONS_UPDATE: + return state.get('focused') + ? state + : state.update('unread', (x) => x + 1); + default: + return state; } } diff --git a/app/javascript/mastodon/scroll.ts b/app/javascript/mastodon/scroll.ts index 1e509c417..625aab0c0 100644 --- a/app/javascript/mastodon/scroll.ts +++ b/app/javascript/mastodon/scroll.ts @@ -1,13 +1,23 @@ -const easingOutQuint = (x: number, t: number, b: number, c: number, d: number) => c * ((t = t / d - 1) * t * t * t * t + 1) + b; -const scroll = (node: Element, key: 'scrollTop' | 'scrollLeft', target: number) => { +const easingOutQuint = ( + x: number, + t: number, + b: number, + c: number, + d: number +) => c * ((t = t / d - 1) * t * t * t * t + 1) + b; +const scroll = ( + node: Element, + key: 'scrollTop' | 'scrollLeft', + target: number +) => { const startTime = Date.now(); - const offset = node[key]; - const gap = target - offset; - const duration = 1000; - let interrupt = false; + const offset = node[key]; + const gap = target - offset; + const duration = 1000; + let interrupt = false; const step = () => { - const elapsed = Date.now() - startTime; + const elapsed = Date.now() - startTime; const percentage = elapsed / duration; if (percentage > 1 || interrupt) { @@ -25,7 +35,14 @@ const scroll = (node: Element, key: 'scrollTop' | 'scrollLeft', target: number) }; }; -const isScrollBehaviorSupported = 'scrollBehavior' in document.documentElement.style; +const isScrollBehaviorSupported = + 'scrollBehavior' in document.documentElement.style; -export const scrollRight = (node: Element, position: number) => isScrollBehaviorSupported ? node.scrollTo({ left: position, behavior: 'smooth' }) : scroll(node, 'scrollLeft', position); -export const scrollTop = (node: Element) => isScrollBehaviorSupported ? node.scrollTo({ top: 0, behavior: 'smooth' }) : scroll(node, 'scrollTop', 0); +export const scrollRight = (node: Element, position: number) => + isScrollBehaviorSupported + ? node.scrollTo({ left: position, behavior: 'smooth' }) + : scroll(node, 'scrollLeft', position); +export const scrollTop = (node: Element) => + isScrollBehaviorSupported + ? node.scrollTo({ top: 0, behavior: 'smooth' }) + : scroll(node, 'scrollTop', 0); diff --git a/app/javascript/mastodon/store/index.ts b/app/javascript/mastodon/store/index.ts index 822c01aa9..6c3e963d9 100644 --- a/app/javascript/mastodon/store/index.ts +++ b/app/javascript/mastodon/store/index.ts @@ -7,17 +7,21 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; export const store = configureStore({ reducer: rootReducer, - middleware: getDefaultMiddleware => - getDefaultMiddleware().concat( - loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] })) + middleware: (getDefaultMiddleware) => + getDefaultMiddleware() + .concat( + loadingBarMiddleware({ + promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'], + }) + ) .concat(errorsMiddleware) .concat(soundsMiddleware()), }); // Infer the `RootState` and `AppDispatch` types from the store itself -export type RootState = ReturnType<typeof rootReducer> +export type RootState = ReturnType<typeof rootReducer>; // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} -export type AppDispatch = typeof store.dispatch +export type AppDispatch = typeof store.dispatch; export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; diff --git a/app/javascript/mastodon/store/middlewares/errors.ts b/app/javascript/mastodon/store/middlewares/errors.ts index b135fa2ee..a5e99d04e 100644 --- a/app/javascript/mastodon/store/middlewares/errors.ts +++ b/app/javascript/mastodon/store/middlewares/errors.ts @@ -5,7 +5,9 @@ import { RootState } from '..'; const defaultFailSuffix = 'FAIL'; export const errorsMiddleware: Middleware<Record<string, never>, RootState> = - ({ dispatch }) => next => action => { + ({ dispatch }) => + (next) => + (action) => { if (action.type && !action.skipAlert) { const isFail = new RegExp(`${defaultFailSuffix}$`, 'g'); diff --git a/app/javascript/mastodon/store/middlewares/loading_bar.ts b/app/javascript/mastodon/store/middlewares/loading_bar.ts index e860b31b6..183c0cf9d 100644 --- a/app/javascript/mastodon/store/middlewares/loading_bar.ts +++ b/app/javascript/mastodon/store/middlewares/loading_bar.ts @@ -3,29 +3,40 @@ import { Middleware } from 'redux'; import { RootState } from '..'; interface Config { - promiseTypeSuffixes?: string[] + promiseTypeSuffixes?: string[]; } -const defaultTypeSuffixes: Config['promiseTypeSuffixes'] = ['PENDING', 'FULFILLED', 'REJECTED']; +const defaultTypeSuffixes: Config['promiseTypeSuffixes'] = [ + 'PENDING', + 'FULFILLED', + 'REJECTED', +]; -export const loadingBarMiddleware = (config: Config = {}): Middleware<Record<string, never>, RootState> => { +export const loadingBarMiddleware = ( + config: Config = {} +): Middleware<Record<string, never>, RootState> => { const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypeSuffixes; - return ({ dispatch }) => next => (action) => { - if (action.type && !action.skipLoading) { - const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes; + return ({ dispatch }) => + (next) => + (action) => { + if (action.type && !action.skipLoading) { + const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes; - const isPending = new RegExp(`${PENDING}$`, 'g'); - const isFulfilled = new RegExp(`${FULFILLED}$`, 'g'); - const isRejected = new RegExp(`${REJECTED}$`, 'g'); + const isPending = new RegExp(`${PENDING}$`, 'g'); + const isFulfilled = new RegExp(`${FULFILLED}$`, 'g'); + const isRejected = new RegExp(`${REJECTED}$`, 'g'); - if (action.type.match(isPending)) { - dispatch(showLoading()); - } else if (action.type.match(isFulfilled) || action.type.match(isRejected)) { - dispatch(hideLoading()); + if (action.type.match(isPending)) { + dispatch(showLoading()); + } else if ( + action.type.match(isFulfilled) || + action.type.match(isRejected) + ) { + dispatch(hideLoading()); + } } - } - return next(action); - }; + return next(action); + }; }; diff --git a/app/javascript/mastodon/store/middlewares/sounds.ts b/app/javascript/mastodon/store/middlewares/sounds.ts index c9d51f857..e7c87df7e 100644 --- a/app/javascript/mastodon/store/middlewares/sounds.ts +++ b/app/javascript/mastodon/store/middlewares/sounds.ts @@ -2,8 +2,8 @@ import { Middleware, AnyAction } from 'redux'; import { RootState } from '..'; interface AudioSource { - src: string - type: string + src: string; + type: string; } const createAudio = (sources: AudioSource[]) => { @@ -30,8 +30,11 @@ const play = (audio: HTMLAudioElement) => { audio.play(); }; -export const soundsMiddleware = (): Middleware<Record<string, never>, RootState> => { - const soundCache: {[key: string]: HTMLAudioElement} = { +export const soundsMiddleware = (): Middleware< + Record<string, never>, + RootState +> => { + const soundCache: { [key: string]: HTMLAudioElement } = { boop: createAudio([ { src: '/sounds/boop.ogg', @@ -44,7 +47,7 @@ export const soundsMiddleware = (): Middleware<Record<string, never>, RootState ]), }; - return () => next => (action: AnyAction) => { + return () => (next) => (action: AnyAction) => { const sound = action?.meta?.sound; if (sound && soundCache[sound]) { diff --git a/app/javascript/mastodon/utils/filters.ts b/app/javascript/mastodon/utils/filters.ts index 5af2aa96a..e5c6422e0 100644 --- a/app/javascript/mastodon/utils/filters.ts +++ b/app/javascript/mastodon/utils/filters.ts @@ -1,16 +1,16 @@ export const toServerSideType = (columnType: string) => { switch (columnType) { - case 'home': - case 'notifications': - case 'public': - case 'thread': - case 'account': - return columnType; - default: - if (columnType.indexOf('list:') > -1) { - return 'home'; - } else { - return 'public'; // community, account, hashtag - } + case 'home': + case 'notifications': + case 'public': + case 'thread': + case 'account': + return columnType; + default: + if (columnType.indexOf('list:') > -1) { + return 'home'; + } else { + return 'public'; // community, account, hashtag + } } }; diff --git a/app/javascript/mastodon/utils/hashtags.ts b/app/javascript/mastodon/utils/hashtags.ts index 358ce37f5..4c76cd7de 100644 --- a/app/javascript/mastodon/utils/hashtags.ts +++ b/app/javascript/mastodon/utils/hashtags.ts @@ -5,17 +5,8 @@ const WORD = '\\p{L}\\p{M}\\p{N}\\p{Pc}'; const buildHashtagPatternRegex = () => { try { return new RegExp( - '(?:^|[^\\/\\)\\w])#((' + - '[' + WORD + '_]' + - '[' + WORD + HASHTAG_SEPARATORS + ']*' + - '[' + ALPHA + HASHTAG_SEPARATORS + ']' + - '[' + WORD + HASHTAG_SEPARATORS +']*' + - '[' + WORD + '_]' + - ')|(' + - '[' + WORD + '_]*' + - '[' + ALPHA + ']' + - '[' + WORD + '_]*' + - '))', 'iu', + `(?:^|[^\\/\\)\\w])#(([${WORD}_][${WORD}${HASHTAG_SEPARATORS}]*[${ALPHA}${HASHTAG_SEPARATORS}][${WORD}${HASHTAG_SEPARATORS}]*[${WORD}_])|([${WORD}_]*[${ALPHA}][${WORD}_]*))`, + 'iu' ); } catch { return /(?:^|[^/)\w])#(\w*[a-zA-Z·]\w*)/i; @@ -25,17 +16,8 @@ const buildHashtagPatternRegex = () => { const buildHashtagRegex = () => { try { return new RegExp( - '^((' + - '[' + WORD + '_]' + - '[' + WORD + HASHTAG_SEPARATORS + ']*' + - '[' + ALPHA + HASHTAG_SEPARATORS + ']' + - '[' + WORD + HASHTAG_SEPARATORS +']*' + - '[' + WORD + '_]' + - ')|(' + - '[' + WORD + '_]*' + - '[' + ALPHA + ']' + - '[' + WORD + '_]*' + - '))$', 'iu', + `^(([${WORD}_][${WORD}${HASHTAG_SEPARATORS}]*[${ALPHA}${HASHTAG_SEPARATORS}][${WORD}${HASHTAG_SEPARATORS}]*[${WORD}_])|([${WORD}_]*[${ALPHA}][${WORD}_]*))$`, + 'iu' ); } catch { return /^(\w*[a-zA-Z·]\w*)$/i; diff --git a/app/javascript/mastodon/utils/numbers.ts b/app/javascript/mastodon/utils/numbers.ts index 35af8a973..a4a028c30 100644 --- a/app/javascript/mastodon/utils/numbers.ts +++ b/app/javascript/mastodon/utils/numbers.ts @@ -21,7 +21,7 @@ const TEN_MILLIONS = DECIMAL_UNITS.MILLION * 10; * shortNumber(5936); * // => [5.936, 1000, 1] */ -export type ShortNumber = [number, DecimalUnits, 0 | 1] // Array of: shorten number, unit of shorten number and maximum fraction digits +export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits export function toShortNumber(sourceNumber: number): ShortNumber { if (sourceNumber < DECIMAL_UNITS.THOUSAND) { return [sourceNumber, DECIMAL_UNITS.ONE, 0]; @@ -38,11 +38,7 @@ export function toShortNumber(sourceNumber: number): ShortNumber { sourceNumber < TEN_MILLIONS ? 1 : 0, ]; } else if (sourceNumber < DECIMAL_UNITS.TRILLION) { - return [ - sourceNumber / DECIMAL_UNITS.BILLION, - DECIMAL_UNITS.BILLION, - 0, - ]; + return [sourceNumber / DECIMAL_UNITS.BILLION, DECIMAL_UNITS.BILLION, 0]; } return [sourceNumber, DECIMAL_UNITS.ONE, 0]; @@ -56,7 +52,10 @@ export function toShortNumber(sourceNumber: number): ShortNumber { * pluralReady(1793, DECIMAL_UNITS.THOUSAND) * // => 1790 */ -export function pluralReady(sourceNumber: number, division: DecimalUnits): number { +export function pluralReady( + sourceNumber: number, + division: DecimalUnits +): number { if (division == null || division < DECIMAL_UNITS.HUNDRED) { return sourceNumber; } diff --git a/app/javascript/mastodon/uuid.ts b/app/javascript/mastodon/uuid.ts index cabbc0f4e..6cadbd6bb 100644 --- a/app/javascript/mastodon/uuid.ts +++ b/app/javascript/mastodon/uuid.ts @@ -1,8 +1,8 @@ export function uuid(a?: string): string { return a ? ( - (a as any as number) ^ + (a as any as number) ^ ((Math.random() * 16) >> ((a as any as number) / 4)) - ).toString(16) + ).toString(16) : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid); } diff --git a/package.json b/package.json index 9129c8b0a..b326dfd45 100644 --- a/package.json +++ b/package.json @@ -180,10 +180,12 @@ "@typescript-eslint/parser": "^5.59.5", "babel-jest": "^29.5.0", "eslint": "^8.39.0", + "eslint-config-prettier": "^8.8.0", "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.27.5", "eslint-plugin-jsdoc": "^43.1.1", "eslint-plugin-jsx-a11y": "~6.7.1", + "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "~6.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/yarn.lock b/yarn.lock index 72fb0a333..cd0abca76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5001,6 +5001,11 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + eslint-import-resolver-node@^0.3.7: version "0.3.7" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" @@ -5091,6 +5096,13 @@ eslint-plugin-jsx-a11y@~6.7.1: object.fromentries "^2.0.6" semver "^6.3.0" +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-promise@~6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" @@ -5430,6 +5442,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" @@ -9199,6 +9216,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.8.8: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"