diff --git a/biome.json b/biome.json index 7df32a8d..3c27a276 100644 --- a/biome.json +++ b/biome.json @@ -52,11 +52,17 @@ "noSvgWithoutTitle": "off" }, "nursery": { - "useImportRestrictions": "off" + "all": true, + "useImportRestrictions": "off", + "useImportType": "off", + "useFilenamingConvention": "off" }, "style": { + "all": true, + "useBlockStatements": "off", + "noImplicitBoolean": "off", "useNamingConvention": "off", - "noUnusedTemplateLiteral": "off" + "noDefaultExport": "off" }, "suspicious": { "noConsoleLog": "off", diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index e12878eb..a8c54c98 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -81,8 +81,8 @@ export const FullArticle = (props: Props) => { const canEdit = () => props.article.authors?.some((a) => Boolean(a) && a?.slug === author()?.slug) const mainTopic = createMemo(() => { - const main_topic_slug = props.article.topics.length > 0 ? props.article.main_topic : null - const mt = props.article.topics.find((tpc: Topic) => tpc.slug === main_topic_slug) + const mainTopicSlug = props.article.topics.length > 0 ? props.article.main_topic : null + const mt = props.article.topics.find((tpc: Topic) => tpc.slug === mainTopicSlug) if (mt) { mt.title = lang() === 'en' ? capitalize(mt.slug.replace(/-/, ' ')) : mt.title return mt diff --git a/src/components/Article/ShoutRatingControl.tsx b/src/components/Article/ShoutRatingControl.tsx index c977e2e3..173016f4 100644 --- a/src/components/Article/ShoutRatingControl.tsx +++ b/src/components/Article/ShoutRatingControl.tsx @@ -52,7 +52,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => { return deleteReaction(reactionToDelete.id) } - const handleRatingChange = async (isUpvote: boolean) => { + const handleRatingChange = (isUpvote: boolean) => { requireAuthentication(async () => { setIsLoading(true) if (isUpvoted()) { diff --git a/src/components/AuthGuard/AuthGuard.tsx b/src/components/AuthGuard/AuthGuard.tsx index f07e3dbf..6ba70476 100644 --- a/src/components/AuthGuard/AuthGuard.tsx +++ b/src/components/AuthGuard/AuthGuard.tsx @@ -15,7 +15,7 @@ export const AuthGuard = (props: Props) => { const { isAuthenticated, isSessionLoaded } = useSession() const { changeSearchParams } = useRouter() - createEffect(async () => { + createEffect(() => { if (props.disabled) { return } diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx index 84f3ba54..35537140 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.tsx +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -51,7 +51,7 @@ export const AuthorBadge = (props: Props) => { const initChat = () => { // eslint-disable-next-line solid/reactivity requireAuthentication(() => { - openPage(router, `inbox`) + openPage(router, 'inbox') changeSearchParams({ initChat: props.author.id.toString(), }) diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 5ffea38d..9fa25a8e 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -2,7 +2,7 @@ import type { Author, Community } from '../../../graphql/schema/core.gen' import { openPage, redirectPage } from '@nanostores/router' import { clsx } from 'clsx' -import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' +import { For, Show, createEffect, createMemo, createSignal, onMount } from 'solid-js' import { useFollowing } from '../../../context/following' import { useLocalize } from '../../../context/localize' @@ -61,7 +61,7 @@ export const AuthorCard = (props: Props) => { const initChat = () => { // eslint-disable-next-line solid/reactivity requireAuthentication(() => { - openPage(router, `inbox`) + openPage(router, 'inbox') changeSearchParams({ initChat: props.author.id.toString(), }) diff --git a/src/components/Discours/Footer.tsx b/src/components/Discours/Footer.tsx index 452d0da0..6a7a0ebc 100644 --- a/src/components/Discours/Footer.tsx +++ b/src/components/Discours/Footer.tsx @@ -89,7 +89,7 @@ export const Footer = () => { }, ]) - const SOCIAL = [ + const social = [ { name: 'facebook', href: 'https://facebook.com/discoursio', @@ -146,7 +146,7 @@ export const Footer = () => { {t('Terms of use')}
- + {(social) => (
diff --git a/src/components/Editor/AudioUploader/AudioUploader.tsx b/src/components/Editor/AudioUploader/AudioUploader.tsx index 59fa52bd..e8eac295 100644 --- a/src/components/Editor/AudioUploader/AudioUploader.tsx +++ b/src/components/Editor/AudioUploader/AudioUploader.tsx @@ -1,4 +1,5 @@ -import { Buffer } from 'buffer' +// biome-ignore lint/nursery/noNodejsModules: TODO: maybe cause problems +import { Buffer } from 'node:buffer' import { clsx } from 'clsx' import { Show } from 'solid-js' diff --git a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx index 3b617f6b..68f687e9 100644 --- a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx +++ b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx @@ -22,12 +22,13 @@ type FloatingMenuProps = { ref: (el: HTMLDivElement) => void } -const embedData = async (data) => { +const embedData = (data) => { const element = document.createRange().createContextualFragment(data) const { attributes } = element.firstChild as HTMLIFrameElement const result: { src: string; width?: string; height?: string } = { src: '' } + // biome-ignore lint/nursery/useForOf: for (let i = 0; i < attributes.length; i++) { const attribute = attributes[i] result[attribute.name] = attribute.value @@ -69,7 +70,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => { .run() } - const validateEmbed = async (value) => { + const validateEmbed = (value) => { const element = document.createRange().createContextualFragment(value) if (element.firstChild?.nodeName !== 'IFRAME') { return t('Error') diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index eb8a22ad..44401b1c 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -216,7 +216,7 @@ const SimplifiedEditor = (props: Props) => { } }) - const handleKeyDown = async (event) => { + const handleKeyDown = (event) => { if (isEmpty() || !isFocused()) { return } diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index 42aa13a3..7be88544 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -71,7 +71,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { } setListBubbleOpen((prev) => !prev) } - const handleKeyDown = async (event) => { + const handleKeyDown = (event) => { if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !props.editor.state.selection.empty) { event.preventDefault() setLinkEditorOpen(true) diff --git a/src/components/Editor/UploadModalContent/UploadModalContent.tsx b/src/components/Editor/UploadModalContent/UploadModalContent.tsx index 03029907..c2e99ad4 100644 --- a/src/components/Editor/UploadModalContent/UploadModalContent.tsx +++ b/src/components/Editor/UploadModalContent/UploadModalContent.tsx @@ -56,7 +56,7 @@ export const UploadModalContent = (props: Props) => { } } - const handleUpload = async () => { + const handleUpload = () => { selectFiles(async ([uploadFile]) => { await runUpload(uploadFile) }) diff --git a/src/components/Editor/VideoUploader/VideoUploader.tsx b/src/components/Editor/VideoUploader/VideoUploader.tsx index 7a8811d0..85261a9d 100644 --- a/src/components/Editor/VideoUploader/VideoUploader.tsx +++ b/src/components/Editor/VideoUploader/VideoUploader.tsx @@ -57,7 +57,7 @@ export const VideoUploader = (props: Props) => { } } - const handleUrlInput = async (value: string) => { + const handleUrlInput = (value: string) => { setError() if (validateUrl(value)) { props.onVideoAdd(composeMediaItems([{ url: value }])) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index d7deab92..f60ec5b5 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -48,7 +48,7 @@ export const LoginForm = () => { setPassword(newPassword) } - const handleSendLinkAgainClick = async (event: Event) => { + const handleSendLinkAgainClick = (event: Event) => { event.preventDefault() setIsLinkSent(true) diff --git a/src/components/Nav/AuthModal/index.tsx b/src/components/Nav/AuthModal/index.tsx index f4fb77ca..c6b03ee8 100644 --- a/src/components/Nav/AuthModal/index.tsx +++ b/src/components/Nav/AuthModal/index.tsx @@ -56,7 +56,7 @@ export const AuthModal = () => { classList={{ [styles.hidden]: mode() !== 'register' && mode() !== 'confirm-email' }} >
-

{t(`Join the global community of authors!`)}

+

{t('Join the global community of authors!')}

{t( 'Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine', diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index 64d1c05c..1d8d8f7b 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -9,7 +9,7 @@ import { useFollowing } from '../../../context/following' import { useLocalize } from '../../../context/localize' import { apiClient } from '../../../graphql/client/core' import { router, useRouter } from '../../../stores/router' -import { loadMyFeed, loadShouts, useArticlesStore } from '../../../stores/zine/articles' +import { loadShouts, useArticlesStore } from '../../../stores/zine/articles' import { loadAuthor, useAuthorsStore } from '../../../stores/zine/authors' import { getImageUrl } from '../../../utils/getImageUrl' import { getDescription } from '../../../utils/meta' @@ -61,7 +61,7 @@ export const AuthorView = (props: Props) => { createEffect(() => { if (author()?.id && !author().stat) { const a = loadAuthor({ slug: '', author_id: author().id }) - console.debug(`[AuthorView] loaded author:`, a) + console.debug('[AuthorView] loaded author:', a) } }) @@ -128,7 +128,7 @@ export const AuthorView = (props: Props) => { const data = await apiClient.getReactionsBy({ by: { comment: false, created_by: commenter.id }, }) - console.debug(`[components.Author] fetched comments`, data) + console.debug('[components.Author] fetched comments', data) setCommented(data) } diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx index 17e83f86..2532da91 100644 --- a/src/components/Views/Edit.tsx +++ b/src/components/Views/Edit.tsx @@ -5,7 +5,7 @@ import { createStore } from 'solid-js/store' import { ShoutForm, useEditorContext } from '../../context/editor' import { useLocalize } from '../../context/localize' -import { type Shout, type Topic } from '../../graphql/schema/core.gen' +import type { Shout, Topic } from '../../graphql/schema/core.gen' import { LayoutType, MediaItem } from '../../pages/types' import { useRouter } from '../../stores/router' import { clone } from '../../utils/clone' @@ -112,7 +112,7 @@ export const EditView = (props: Props) => { const handleBeforeUnload = (event) => { if (!deepEqual(prevForm, form)) { event.returnValue = t( - `There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?`, + 'There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?', ) } } diff --git a/src/components/Views/Feed/Feed.tsx b/src/components/Views/Feed/Feed.tsx index 4c1f55bc..a1010684 100644 --- a/src/components/Views/Feed/Feed.tsx +++ b/src/components/Views/Feed/Feed.tsx @@ -7,12 +7,7 @@ import { useLocalize } from '../../../context/localize' import { useReactions } from '../../../context/reactions' import { useSession } from '../../../context/session' import { apiClient } from '../../../graphql/client/core' -import { - type Author, - type LoadShoutsOptions, - type Reaction, - type Shout, -} from '../../../graphql/schema/core.gen' +import type { Author, LoadShoutsOptions, Reaction, Shout } from '../../../graphql/schema/core.gen' import { router, useRouter } from '../../../stores/router' import { showModal } from '../../../stores/ui' import { resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles' diff --git a/src/components/Views/Inbox/Inbox.tsx b/src/components/Views/Inbox/Inbox.tsx index a654b37c..f74bd32f 100644 --- a/src/components/Views/Inbox/Inbox.tsx +++ b/src/components/Views/Inbox/Inbox.tsx @@ -9,7 +9,6 @@ import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { useRouter } from '../../../stores/router' import { showModal } from '../../../stores/ui' -import { useAuthorsStore } from '../../../stores/zine/authors' import SimplifiedEditor from '../../Editor/SimplifiedEditor' import DialogCard from '../../Inbox/DialogCard' import DialogHeader from '../../Inbox/DialogHeader' @@ -83,7 +82,7 @@ export const InboxView = (props: Props) => { } } - const handleSubmit = async (message: string) => { + const handleSubmit = (message: string) => { sendMessage({ body: message, chat_id: currentDialog()?.id.toString(), diff --git a/src/components/Views/Search.tsx b/src/components/Views/Search.tsx index a2cec274..a5308361 100644 --- a/src/components/Views/Search.tsx +++ b/src/components/Views/Search.tsx @@ -50,7 +50,7 @@ export const SearchView = (props: Props) => { restoreScrollPosition() } - onMount(async () => { + onMount(() => { const q = window.location.pathname.replace('/search/', '') || props.query setQuery(q) searchEl.value = q diff --git a/src/components/Views/Topic.tsx b/src/components/Views/Topic.tsx index 6f9a6cf3..34d2683b 100644 --- a/src/components/Views/Topic.tsx +++ b/src/components/Views/Topic.tsx @@ -74,7 +74,7 @@ export const TopicView = (props: Props) => { restoreScrollPosition() } - onMount(async () => { + onMount(() => { if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { loadMore() } diff --git a/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx b/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx index 972768cc..b5096d6b 100644 --- a/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx +++ b/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx @@ -33,7 +33,7 @@ const GrowingTextarea = (props: Props) => { props.value(textareaValue) } - const handleKeyDown = async (event) => { + const handleKeyDown = (event) => { if (event.key === 'Enter' && event.shiftKey) { return } diff --git a/src/components/_shared/SolidSwiper/EditorSwiper.tsx b/src/components/_shared/SolidSwiper/EditorSwiper.tsx index ed34743e..c7f53a03 100644 --- a/src/components/_shared/SolidSwiper/EditorSwiper.tsx +++ b/src/components/_shared/SolidSwiper/EditorSwiper.tsx @@ -84,7 +84,7 @@ export const EditorSwiper = (props: Props) => { const { selectFiles } = createFileUploader({ multiple: true, - accept: `image/*`, + accept: 'image/*', }) const initUpload = async (selectedFiles) => { diff --git a/src/components/_shared/SolidSwiper/swiper.d.ts b/src/components/_shared/SolidSwiper/swiper.d.ts index aa4e4685..ad197187 100644 --- a/src/components/_shared/SolidSwiper/swiper.d.ts +++ b/src/components/_shared/SolidSwiper/swiper.d.ts @@ -27,6 +27,8 @@ type KebabObjectKeys = { type SwiperRef = HTMLElement & { swiper: Swiper; initialize: () => void } declare module 'solid-js' { + // biome-ignore lint/style/useNamingConvention: JSX is ok + // biome-ignore lint/style/noNamespace: TODO: explain why namespace JSX { interface IntrinsicElements { 'swiper-container': SwiperContainerAttributes diff --git a/src/context/connect.tsx b/src/context/connect.tsx index a86f443f..d10d0695 100644 --- a/src/context/connect.tsx +++ b/src/context/connect.tsx @@ -29,7 +29,7 @@ export interface ConnectContextType { const ConnectContext = createContext() export const ConnectProvider = (props: { children: JSX.Element }) => { - const [messageHandlers, setHandlers] = createSignal>([]) + const [messageHandlers, setHandlers] = createSignal([]) // const [messages, setMessages] = createSignal>([]); const [connected, setConnected] = createSignal(false) const { session } = useSession() diff --git a/src/context/inbox.tsx b/src/context/inbox.tsx index e1124c2a..02bb9477 100644 --- a/src/context/inbox.tsx +++ b/src/context/inbox.tsx @@ -13,10 +13,10 @@ type InboxContextType = { chats: Accessor messages?: Accessor createChat: (members: number[], title: string) => Promise<{ chat: Chat }> - loadChats: () => Promise> - loadRecipients: () => Array - loadMessages: (by: MessagesBy, limit: number, offset: number) => Promise> - getMessages?: (chatId: string) => Promise> + loadChats: () => Promise + loadRecipients: () => Author[] + loadMessages: (by: MessagesBy, limit: number, offset: number) => Promise + getMessages?: (chatId: string) => Promise sendMessage?: (args: MutationCreate_MessageArgs) => void } @@ -47,7 +47,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => { const { addHandler } = useConnect() addHandler(handleMessage) - const loadMessages = async (by: MessagesBy, limit = 50, offset = 0): Promise> => { + const loadMessages = async (by: MessagesBy, limit = 50, offset = 0): Promise => { if (inboxClient.private) { const msgs = await inboxClient.loadChatMessages({ by, limit, offset }) setMessages((mmm) => [...new Set([...mmm, ...msgs])]) diff --git a/src/context/notifications.tsx b/src/context/notifications.tsx index 5d793a5a..36507e17 100644 --- a/src/context/notifications.tsx +++ b/src/context/notifications.tsx @@ -58,7 +58,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { setTotalNotificationsCount(total) setUnreadNotificationsCount(unread) setNotificationEntities(newGroupsEntries) - console.debug(`[context.notifications] groups updated`, groups) + console.debug('[context.notifications] groups updated', groups) return groups } return [] @@ -75,7 +75,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { onMount(() => { addHandler((data: SSEMessage) => { if (data.entity === 'reaction' && isAuthenticated()) { - console.info(`[context.notifications] event`, data) + console.info('[context.notifications] event', data) loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) }) } }) diff --git a/src/context/profile.tsx b/src/context/profile.tsx index 33cfcac5..1b88f149 100644 --- a/src/context/profile.tsx +++ b/src/context/profile.tsx @@ -38,7 +38,7 @@ export const ProfileFormProvider = (props: { children: JSX.Element }) => { } } - createEffect(async () => { + createEffect(() => { if (author()) { const currentAuthor = author() setForm({ diff --git a/src/context/session.tsx b/src/context/session.tsx index f5d70567..18ae19c2 100644 --- a/src/context/session.tsx +++ b/src/context/session.tsx @@ -5,7 +5,6 @@ import type { Author } from '../graphql/schema/core.gen' import { ApiResponse, AuthToken, - AuthorizeResponse, Authorizer, ConfigType, ForgotPasswordInput, @@ -71,6 +70,7 @@ export type SessionContextType = { authorizer: () => Authorizer } +// biome-ignore lint/nursery/noEmptyBlockStatements: noop const noop = () => {} const SessionContext = createContext() @@ -257,7 +257,7 @@ export const SessionProvider = (props: { ), ) - const [authCallback, setAuthCallback] = createSignal<() => void>(() => {}) + const [authCallback, setAuthCallback] = createSignal<() => void>(noop) const requireAuthentication = (callback: () => void, modalSource: AuthModalSource) => { setAuthCallback((_cb) => callback) if (!session()) { @@ -285,13 +285,8 @@ export const SessionProvider = (props: { } return { data: resp?.data, errors: resp?.errors } } - const signUp = async (params: SignupInput) => { - return authenticate(authorizer().signup, params) - } - - const signIn = async (params: LoginInput) => { - return authenticate(authorizer().login, params) - } + const signUp = async (params: SignupInput) => await authenticate(authorizer().signup, params) + const signIn = async (params: LoginInput) => await authenticate(authorizer().login, params) const signOut = async () => { const authResult: ApiResponse = await authorizer().logout() diff --git a/src/pages/drafts.page.route.ts b/src/pages/drafts.page.route.ts index bb4d2892..451f82b0 100644 --- a/src/pages/drafts.page.route.ts +++ b/src/pages/drafts.page.route.ts @@ -1,4 +1,6 @@ import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' +import { getServerRoute as gsr } from '../utils/getServerRoute' -export default getServerRoute(ROUTES.drafts) +const getServerRoute = () => gsr(ROUTES.drafts) + +export { getServerRoute } diff --git a/src/pages/search.page.server.ts b/src/pages/search.page.server.ts index 22a337b1..8f3e3177 100644 --- a/src/pages/search.page.server.ts +++ b/src/pages/search.page.server.ts @@ -6,7 +6,7 @@ import { SearchResult } from '../graphql/schema/core.gen' export const onBeforeRender = async (pageContext: PageContext) => { const { q: text } = pageContext.routeParams - const searchResults: Array = await apiClient.getShoutsSearch({ text, limit: 50 }) + const searchResults: SearchResult[] = await apiClient.getShoutsSearch({ text, limit: 50 }) const pageProps: PageProps = { searchResults, seo: { title: '' } } return { diff --git a/src/utils/getServerRoute.ts b/src/utils/getServerRoute.ts index 4e10ccc8..d028ccc6 100644 --- a/src/utils/getServerRoute.ts +++ b/src/utils/getServerRoute.ts @@ -1,4 +1,6 @@ // /:slug -> /@slug // https://vike.dev/routing // https://www.npmjs.com/package/@nanostores/router -export const getServerRoute = (clientRoute: string) => clientRoute.replaceAll(':', '@') +import { ROUTES } from '../stores/router' + +export const getServerRoute = (clientRoute: string = ROUTES) => clientRoute.replaceAll(':', '@')