locale !== currentLocale); - if (!supportedLanguages.length) return null; + if (supportedLanguages.length === 0) { + return null; + } return ( diff --git a/apps/documentation/app/[locale]/[project]/page.tsx b/apps/documentation/app/[locale]/[project]/page.tsx index ea4c6d18..924382c8 100644 --- a/apps/documentation/app/[locale]/[project]/page.tsx +++ b/apps/documentation/app/[locale]/[project]/page.tsx @@ -17,7 +17,9 @@ export default async function Page({ params }: PageProps) { project, }); - if (document === null) notFound(); + if (document === null) { + notFound(); + } return ( cur === locale); - if (!isValidLocale) notFound(); + if (!isValidLocale) { + notFound(); + } // setting setRequestLocale to support next-intl for static rendering setRequestLocale(locale); @@ -64,12 +65,12 @@ export default async function MainLayout({ children, params: { locale } }: MainL } return ( - + - + {children} - + diff --git a/apps/documentation/app/icon.svg b/apps/documentation/app/icon.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/app/icon.svg +++ b/apps/documentation/app/icon.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/app/not-found.tsx b/apps/documentation/app/not-found.tsx index 67fbc611..a6b7634a 100644 --- a/apps/documentation/app/not-found.tsx +++ b/apps/documentation/app/not-found.tsx @@ -13,7 +13,7 @@ const quicksand = Quicksand({ export default function NotFound() { return ( - + 404 - Not found diff --git a/apps/documentation/components/DocSearchComponent.tsx b/apps/documentation/components/DocSearchComponent.tsx index 87de1f36..cb842c65 100644 --- a/apps/documentation/components/DocSearchComponent.tsx +++ b/apps/documentation/components/DocSearchComponent.tsx @@ -1,10 +1,10 @@ "use client"; -import { DocSearch } from "@docsearch/react"; -import { useLocale, useTranslations } from "next-intl"; -import "@docsearch/css"; import { inputVariants } from "@codaco/ui"; +import "@docsearch/css"; +import { DocSearch } from "@docsearch/react"; import { Search } from "lucide-react"; +import { useLocale, useTranslations } from "next-intl"; import { env } from "~/env"; import { cn } from "~/lib/utils"; diff --git a/apps/documentation/components/Hero.tsx b/apps/documentation/components/Hero.tsx index 27a4b6a7..7ac9240a 100644 --- a/apps/documentation/components/Hero.tsx +++ b/apps/documentation/components/Hero.tsx @@ -58,7 +58,7 @@ export function Hero() { {t("Hero.title")} {t("Hero.tagline")} - + {resolvedTheme !== "dark" && ( diff --git a/apps/documentation/components/ProjectSwitcher.tsx b/apps/documentation/components/ProjectSwitcher.tsx index e614c5d6..94c8a9e1 100644 --- a/apps/documentation/components/ProjectSwitcher.tsx +++ b/apps/documentation/components/ProjectSwitcher.tsx @@ -68,7 +68,7 @@ export default function ProjectSwitcher() { {projects.map((p) => ( - + ))} diff --git a/apps/documentation/components/SharedNav/Menu.tsx b/apps/documentation/components/SharedNav/Menu.tsx index b17b6c3e..c0be451d 100644 --- a/apps/documentation/components/SharedNav/Menu.tsx +++ b/apps/documentation/components/SharedNav/Menu.tsx @@ -54,7 +54,7 @@ export const NavigationMenuDemo = () => { const t = useTranslations("SharedNavigation"); return ( - + {links.map((link, i) => { @@ -62,13 +62,13 @@ export const NavigationMenuDemo = () => { return ( - {t(link.translationKey)} + {t(link.translationKey)} {link.menu.map((subLink, i) => ( - + { const t = useTranslations("SharedNavigation"); const [submenu, setSubmenu] = useState([]); - if (!submenu.length) { + if (submenu.length === 0) { return ( {links.map((link, i) => { diff --git a/apps/documentation/components/Sidebar.tsx b/apps/documentation/components/Sidebar.tsx index 5917dbb2..a48aa40c 100644 --- a/apps/documentation/components/Sidebar.tsx +++ b/apps/documentation/components/Sidebar.tsx @@ -5,7 +5,7 @@ import { useLocale } from "next-intl"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { type RefObject, useEffect, useMemo, useRef, useState } from "react"; -import type { Locale, Project, SidebarPage, TSideBar, SidebarFolder as TSidebarFolder } from "~/app/types"; +import type { Locale, Project, SidebarPage, TSideBar, SidebarFolder as tSidebarFolder } from "~/app/types"; import { cn } from "~/lib/utils"; import sidebarData from "~/public/sidebar.json"; import DocSearchComponent from "./DocSearchComponent"; @@ -21,7 +21,7 @@ const MotionChevron = motion.create(ChevronRight); * @param sidebarItems {(TSidebarFolder | SidebarPage)[]} * @returns {(TSidebarFolder | SidebarPage)[]} */ -const sortSidebarItems = (sidebarItems: (TSidebarFolder | SidebarPage)[]) => +const sortSidebarItems = (sidebarItems: (tSidebarFolder | SidebarPage)[]) => sidebarItems.sort((a, b) => { // If both items have navOrder, sort by that if (a.navOrder !== null && b.navOrder !== null) { @@ -39,16 +39,20 @@ const sortSidebarItems = (sidebarItems: (TSidebarFolder | SidebarPage)[]) => return a.label.localeCompare(b.label); }); +const pathSeparatorRegex = /[\\/]/; + export function processSourceFile(type: "page", locale: Locale, sourceFile: string): string; export function processSourceFile(type: "folder", locale: Locale, sourceFile: string | undefined): string | undefined; // Used by sidebar to process sourceFile values into usable routes export function processSourceFile(type: "folder" | "page", locale: Locale, sourceFile?: string) { - if (!sourceFile) return; + if (!sourceFile) { + return; + } // We can't use path.sep because the webpack node shim always returns '/'. // Because this might be running on Windows, we need to use a regex to split // by either / or \. - const pathSegments = sourceFile.split(/[\\/]/).slice(2); + const pathSegments = sourceFile.split(pathSeparatorRegex).slice(2); let returnPath = ""; @@ -85,9 +89,13 @@ const SidebarFolder = ({ const pathname = usePathname(); const memoizedIsOpen = useMemo(() => { - if (alwaysOpen) return true; + if (alwaysOpen) { + return true; + } - if (defaultOpen) return true; + if (defaultOpen) { + return true; + } return (children as React.ReactElement<{ href?: string }>[]).some((child) => child.props.href === pathname); }, [alwaysOpen, defaultOpen, children, pathname]); @@ -103,7 +111,9 @@ const SidebarFolder = ({ defaultOpen={defaultOpen ?? alwaysOpen} open={isOpen} onOpenChange={() => { - if (alwaysOpen) return; + if (alwaysOpen) { + return; + } setIsOpen(!isOpen); }} className={cn("my-4 flex flex-col")} @@ -113,7 +123,7 @@ const SidebarFolder = ({ "focusable my-1 flex flex-1 items-center justify-between text-balance text-base font-semibold capitalize", !alwaysOpen && "cursor-pointer", )} - asChild + asChild={true} > {href ? ( @@ -123,7 +133,7 @@ const SidebarFolder = ({ className="h-4 w-4" initial={{ rotate: isOpen ? 90 : 0 }} animate={{ rotate: isOpen ? 90 : 0 }} - aria-hidden + aria-hidden={true} /> )} @@ -135,7 +145,7 @@ const SidebarFolder = ({ className="h-4 w-4" initial={{ rotate: isOpen ? 90 : 0 }} animate={{ rotate: isOpen ? 90 : 0 }} - aria-hidden + aria-hidden={true} /> )} @@ -143,7 +153,7 @@ const SidebarFolder = ({ @@ -192,7 +202,7 @@ const SidebarLink = ({ }; const renderSidebarItem = ( - item: TSidebarFolder | SidebarPage, + item: tSidebarFolder | SidebarPage, locale: Locale, sidebarContainerRef: RefObject, ) => { diff --git a/apps/documentation/components/TableOfContents.tsx b/apps/documentation/components/TableOfContents.tsx index b01c5711..36e5ae48 100644 --- a/apps/documentation/components/TableOfContents.tsx +++ b/apps/documentation/components/TableOfContents.tsx @@ -7,7 +7,7 @@ import useHighlighted from "~/hooks/useHighlighted"; import type { HeadingNode } from "~/lib/tableOfContents"; import { cn } from "~/lib/utils"; -const TOCLink = ({ +const tocLink = ({ node, sideBar, }: { @@ -18,7 +18,9 @@ const TOCLink = ({ const [highlighted] = useHighlighted(node.id); useEffect(() => { - if (!sideBar) return; + if (!sideBar) { + return; + } if (highlighted && ref.current) { ref.current.scrollIntoView({ @@ -73,7 +75,7 @@ function renderNodes(nodes: HeadingNode[], sideBar: boolean) { {nodes.map((node) => ( - + {node.children?.length > 0 && renderNodes(node.children, sideBar)} ))} diff --git a/apps/documentation/components/ai-assistant.tsx b/apps/documentation/components/ai-assistant.tsx index 9fdf8e2d..db4b644f 100644 --- a/apps/documentation/components/ai-assistant.tsx +++ b/apps/documentation/components/ai-assistant.tsx @@ -41,7 +41,7 @@ const TriggerButton = () => { return ( - + } {content} - {showToc && } + {showToc && } > ); } diff --git a/apps/documentation/components/customComponents/CodeCopyButton.tsx b/apps/documentation/components/customComponents/CodeCopyButton.tsx index 06d9a5b7..85fba26d 100644 --- a/apps/documentation/components/customComponents/CodeCopyButton.tsx +++ b/apps/documentation/components/customComponents/CodeCopyButton.tsx @@ -13,15 +13,13 @@ const CodeCopyButton = ({ code }: { code: string }) => { await navigator.clipboard.writeText(text); setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); // Reset state after 2 seconds - } catch (error) { - console.error("Failed to copy to clipboard:", error); - } + } catch (error) {} }; return ( - - + + {isCopied ? ( diff --git a/apps/documentation/components/customComponents/VideoIFrame.tsx b/apps/documentation/components/customComponents/VideoIFrame.tsx index 0a81e9da..01c4d5b5 100644 --- a/apps/documentation/components/customComponents/VideoIFrame.tsx +++ b/apps/documentation/components/customComponents/VideoIFrame.tsx @@ -1,5 +1,5 @@ const VideoIFrame = ({ src, title }: { src: string; title: string }) => { - return ; + return ; }; export default VideoIFrame; diff --git a/apps/documentation/lib/docs.tsx b/apps/documentation/lib/docs.tsx index 7479ac26..900cfde2 100644 --- a/apps/documentation/lib/docs.tsx +++ b/apps/documentation/lib/docs.tsx @@ -1,9 +1,10 @@ -import { existsSync, readFileSync } from "node:fs"; -import { join, sep } from "node:path"; import { Button, Details, Heading, ListItem, OrderedList, Paragraph, Summary, UnorderedList } from "@codaco/ui"; import rehypeFigure from "@microflash/rehype-figure"; import type { LinkProps } from "next/link"; +import { existsSync, readFileSync } from "node:fs"; +import { join, sep } from "node:path"; import type { ReactNode } from "react"; +// biome-ignore lint/style/noNamespaceImport: workaround import * as prod from "react/jsx-runtime"; import rehypeHighlight from "rehype-highlight"; import rehypeRaw from "rehype-raw"; @@ -32,7 +33,6 @@ import KeyConcept from "~/components/customComponents/KeyConcept"; import Pre from "~/components/customComponents/Pre"; import { PrerequisitesSection, SummaryCard, SummarySection } from "~/components/customComponents/SummaryCard"; import TipBox, { type TipBoxProps } from "~/components/customComponents/TipBox"; -import VideoIFrame from "~/components/customComponents/VideoIFrame"; import sidebar from "~/public/sidebar.json"; import { get } from "./helper_functions"; import processPreTags from "./processPreTags"; @@ -113,7 +113,6 @@ export const getDocsForRouteSegment = ({ const sidebarData = get(typedSidebar, [locale, project], null) as SidebarProject; if (!sidebarData) { - console.log(`No sidebar data found for ${locale} and ${project}`); return []; } @@ -159,7 +158,9 @@ export const getDocsForRouteSegment = ({ export const getSourceFile = (locale: string, project: string, pathSegment?: string[]) => { const projectSourceFile = get(sidebar, [locale, project, "sourceFile"], null) as string; - if (!pathSegment) return join(process.cwd(), projectSourceFile); + if (!pathSegment) { + return join(process.cwd(), projectSourceFile); + } const pathSegmentWithChildren = pathSegment.flatMap((segment, index) => { if (index === 0) { @@ -175,7 +176,9 @@ export const getSourceFile = (locale: string, project: string, pathSegment?: str null, ) as string | null; - if (!folderSourceFile) return null; + if (!folderSourceFile) { + return null; + } return join(process.cwd(), folderSourceFile); }; @@ -192,7 +195,6 @@ export async function getDocumentForPath({ const sourceFile = getSourceFile(locale, project, pathSegment); if (!sourceFile || (sourceFile && !existsSync(sourceFile))) { - console.log(`File not found: ${sourceFile}`); return null; } @@ -261,7 +263,7 @@ export async function getDocumentForPath({ keyconcept: (props: { title: string; children: ReactNode }) => , goodpractice: (props: { children: ReactNode }) => , badpractice: (props: { children: ReactNode }) => , - videoiframe: (props: { src: string; title: string }) => , + videoiframe: (props: { src: string; title: string }) => , summarycard: (props: { duration: string; children: ReactNode }) => , prerequisitessection: (props: { children: ReactNode }) => , summarysection: (props: { children: ReactNode }) => , diff --git a/apps/documentation/lib/helper_functions.ts b/apps/documentation/lib/helper_functions.ts index 5c896b6b..66f7ec1c 100644 --- a/apps/documentation/lib/helper_functions.ts +++ b/apps/documentation/lib/helper_functions.ts @@ -55,7 +55,9 @@ export const getNestedPath = (path: string) => { // it might not work for some edge cases. Test your code! export const get = (obj: Record, path: string | string[], defValue: unknown = undefined) => { // If path is not defined or it has false value - if (!path) return undefined; + if (!path) { + return undefined; + } // Check if path is string or array. Regex : ensure that we do not have '.' and brackets. // Regex explained: https://regexr.com/58j0k const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); diff --git a/apps/documentation/lib/processPreTags.ts b/apps/documentation/lib/processPreTags.ts index f2e12e6b..e22b2c39 100644 --- a/apps/documentation/lib/processPreTags.ts +++ b/apps/documentation/lib/processPreTags.ts @@ -18,7 +18,9 @@ const processPreTags = () => { return (tree: Tree) => { visit(tree, { tagName: "pre" }, (node) => { const codeElement = node.children.find((child) => child.tagName === "code"); - if (!codeElement) return; + if (!codeElement) { + return; + } // Extract the text content from the `code` element const rawCodeContent = hastNodeToString(codeElement); diff --git a/apps/documentation/lib/writeSidebarJson.ts b/apps/documentation/lib/writeSidebarJson.ts index a6f0d6ba..722685cb 100644 --- a/apps/documentation/lib/writeSidebarJson.ts +++ b/apps/documentation/lib/writeSidebarJson.ts @@ -60,16 +60,15 @@ function generateSidebarData() { } // Only process files ending in .md or .mdx - if (!file.name.endsWith(".md") && !file.name.endsWith(".mdx")) return; + if (!file.name.endsWith(".md") && !file.name.endsWith(".mdx")) { + return; + } // Determine locale based on file name (format is `index.en.mdx` or `index.en.md`) const locale = file.name.split(".")[1] as Locale | undefined; // If there's no locale, or the locale isn't included in the type, ignore it. if (!locale || !locales.includes(locale as Locale)) { - console.warn( - `File ${file.name} is missing a locale or has a locale not defined in Locale. Locale is ${locale}. Skipping.`, - ); return; } @@ -83,7 +82,9 @@ function generateSidebarData() { const matterResult = matter(markdownFile); // If file has "hidden: true" in frontmatter, skip it - if (matterResult.data.hidden) return; + if (matterResult.data.hidden) { + return; + } set(sidebarData[locale], [...nestedPath, key], createPageEntry(file, matterResult)); } @@ -96,5 +97,7 @@ try { writeFileSync(join(process.cwd(), "public", "sidebar.json"), JSON.stringify(sidebarData, null, 2), "utf-8"); } catch (e) { - console.log("Error writing sidebar data!", e); + // biome-ignore lint/suspicious/noConsole: + console.error(e); + process.exit(1); } diff --git a/apps/documentation/public/algolia-logo.svg b/apps/documentation/public/algolia-logo.svg index 44e9942d..2a5d5e63 100644 --- a/apps/documentation/public/algolia-logo.svg +++ b/apps/documentation/public/algolia-logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/logo-inline.svg b/apps/documentation/public/assets/img/logo-inline.svg index 9c3e33d8..d5058852 100644 --- a/apps/documentation/public/assets/img/logo-inline.svg +++ b/apps/documentation/public/assets/img/logo-inline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/logo.svg b/apps/documentation/public/assets/img/logo.svg index 0ad76d88..f8cbf280 100644 --- a/apps/documentation/public/assets/img/logo.svg +++ b/apps/documentation/public/assets/img/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/sidebar-bg-cropped.svg b/apps/documentation/public/assets/img/sidebar-bg-cropped.svg index f84765cb..b015ae8e 100644 --- a/apps/documentation/public/assets/img/sidebar-bg-cropped.svg +++ b/apps/documentation/public/assets/img/sidebar-bg-cropped.svg @@ -1 +1 @@ -sidebar-bg-cropped \ No newline at end of file +sidebar-bg-cropped \ No newline at end of file diff --git a/apps/documentation/public/assets/img/tip-caution.svg b/apps/documentation/public/assets/img/tip-caution.svg index 49535f4b..dd2e7921 100644 --- a/apps/documentation/public/assets/img/tip-caution.svg +++ b/apps/documentation/public/assets/img/tip-caution.svg @@ -1 +1 @@ -Warning \ No newline at end of file +Warning \ No newline at end of file diff --git a/apps/documentation/public/assets/img/tip-info.svg b/apps/documentation/public/assets/img/tip-info.svg index c77c1b4c..e590d33d 100644 --- a/apps/documentation/public/assets/img/tip-info.svg +++ b/apps/documentation/public/assets/img/tip-info.svg @@ -1 +1 @@ -Info \ No newline at end of file +Info \ No newline at end of file diff --git a/apps/documentation/public/favicons/icon.svg b/apps/documentation/public/favicons/icon.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/public/favicons/icon.svg +++ b/apps/documentation/public/favicons/icon.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/public/images/key-concept.svg b/apps/documentation/public/images/key-concept.svg index 2d458866..a3db2199 100644 --- a/apps/documentation/public/images/key-concept.svg +++ b/apps/documentation/public/images/key-concept.svg @@ -6,6 +6,6 @@ .st0{fill:#DEA800;} .st1{fill:#F2B700;} - - + + diff --git a/apps/documentation/public/images/mark.svg b/apps/documentation/public/images/mark.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/public/images/mark.svg +++ b/apps/documentation/public/images/mark.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/public/images/robot.svg b/apps/documentation/public/images/robot.svg index 4efe235a..54577b3f 100644 --- a/apps/documentation/public/images/robot.svg +++ b/apps/documentation/public/images/robot.svg @@ -28,56 +28,56 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/documentation/public/images/tip-caution.svg b/apps/documentation/public/images/tip-caution.svg index d5388560..43b64186 100644 --- a/apps/documentation/public/images/tip-caution.svg +++ b/apps/documentation/public/images/tip-caution.svg @@ -9,22 +9,22 @@ .st3{fill:#F22F66;} - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/apps/documentation/public/images/tip-info.svg b/apps/documentation/public/images/tip-info.svg index c77c1b4c..e590d33d 100644 --- a/apps/documentation/public/images/tip-info.svg +++ b/apps/documentation/public/images/tip-info.svg @@ -1 +1 @@ -Info \ No newline at end of file +Info \ No newline at end of file diff --git a/apps/documentation/public/images/typemark-negative.svg b/apps/documentation/public/images/typemark-negative.svg index 96a0954a..35c15fdd 100644 --- a/apps/documentation/public/images/typemark-negative.svg +++ b/apps/documentation/public/images/typemark-negative.svg @@ -15,34 +15,34 @@ - - + + - - + + - - + - - + - - - - - - - - - - - - - - + + - - + + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/biome.json b/biome.json index cd382fd6..3b90cb66 100644 --- a/biome.json +++ b/biome.json @@ -7,8 +7,33 @@ "enabled": true, "rules": { "recommended": true, + "performance": { + "all": true + }, + "security": { + "all": true + }, + "style": { + "all": true, + "useNamingConvention": "off", + "useFilenamingConvention": "off", + "noDefaultExport": "off" + }, + "suspicious": { + "all": true, + "noReactSpecificProps": "off" + }, "correctness": { "noUnusedImports": "error" + }, + "a11y": { + "all": true + }, + "nursery": { + "useGoogleFontDisplay": "error", + "noDocumentImportInPage": "error", + "noHeadElement": "error", + "noHeadImportInDocument": "error" } } }, diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts index 171619dd..f0bfd706 100644 --- a/packages/analytics/src/index.ts +++ b/packages/analytics/src/index.ts @@ -88,12 +88,8 @@ export const createRouteHandler = ({ // Check if analytics is disabled if (disableAnalytics) { + // biome-ignore lint/suspicious/noConsole: console.info("🛑 Analytics disabled. Payload not sent."); - try { - console.info("Payload:", "\n", JSON.stringify(incomingEvent, null, 2)); - } catch (e) { - console.error("Error stringifying payload:", e); - } return NextResponse.json({ message: "Analytics disabled" }, { status: 200 }); } @@ -102,14 +98,13 @@ export const createRouteHandler = ({ const trackableEvent = TrackableEventSchema.safeParse(incomingEvent); if (!trackableEvent.success) { - console.error("Invalid event:", trackableEvent.error); return NextResponse.json({ error: "Invalid event" }, { status: 400 }); } // We don't want failures in third party services to prevent us from // tracking analytics events, so we'll catch any errors and log them // and continue with an 'Unknown' country code. - let countryISOCode = "Unknown"; + let countryIsoCode = "Unknown"; try { const ip = await fetch("https://api64.ipify.org").then((res) => res.text()); @@ -120,18 +115,20 @@ export const createRouteHandler = ({ const geoData = (await fetch(`http://ip-api.com/json/${ip}`).then((res) => res.json())) as GeoData; if (geoData.status === "success") { - countryISOCode = geoData.countryCode; + countryIsoCode = geoData.countryCode; } else { throw new Error(geoData.message); } } catch (e) { - console.error("Geolocation failed:", e); + const error = ensureError(e); + // biome-ignore lint/suspicious/noConsole: + console.error(`Error fetching country code: ${error.message}`); } const analyticsEvent: analyticsEvent = { ...trackableEvent.data, installationId, - countryISOCode, + countryISOCode: countryIsoCode, }; // Forward to backend @@ -159,8 +156,6 @@ export const createRouteHandler = ({ if (response.status === 500) { error = "Analytics platform returned an internal server error. Please check the platform logs."; } - - console.info(`⚠️ Analytics platform rejected event: ${error}`); return Response.json( { error, @@ -168,11 +163,9 @@ export const createRouteHandler = ({ { status: 500 }, ); } - console.info("🚀 Analytics event sent to platform!"); return Response.json({ message: "Event forwarded successfully" }); } catch (e) { const error = ensureError(e); - console.info("🚫 Internal error with sending analytics event."); return Response.json({ error: `Error in analytics route handler: ${error.message}` }, { status: 500 }); } diff --git a/packages/analytics/src/utils.ts b/packages/analytics/src/utils.ts index d562793e..d49e6aa0 100644 --- a/packages/analytics/src/utils.ts +++ b/packages/analytics/src/utils.ts @@ -1,17 +1,23 @@ // Helper function that ensures that a value is an Error export function ensureError(value: unknown): Error { - if (!value) return new Error("No value was thrown"); + if (!value) { + return new Error("No value was thrown"); + } - if (value instanceof Error) return value; + if (value instanceof Error) { + return value; + } // Test if value inherits from Error - if (Object.prototype.isPrototypeOf.call(value, Error)) return value as Error & typeof value; + if (Object.prototype.isPrototypeOf.call(value, Error)) { + return value as Error & typeof value; + } let stringified = "[Unable to stringify the thrown value]"; try { stringified = JSON.stringify(value); } catch (e) { - console.error(e); + // Do nothing } const error = new Error(`This value was thrown as is, not through an Error: ${stringified}`); diff --git a/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx b/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx index f8b8eabe..59144ba9 100644 --- a/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx +++ b/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx @@ -1,6 +1,6 @@ "use client"; -import * as blobs2 from "blobs/v2"; +import { svgPath } from "blobs/v2"; import { interpolatePath } from "d3-interpolate-path"; import { memo, useMemo } from "react"; import Canvas from "./Canvas"; @@ -32,7 +32,7 @@ const gradients = [ const DEFAULT_SPEED_FACTOR = 1; -class NCBlob { +class NcBlob { layer: 1 | 2 | 3; speed: number; angle: number; @@ -168,14 +168,14 @@ class NCBlob { this.positionY = randomInt(0 - this.size / 2, this.canvasHeight - this.size / 2); // Create two random shapes to interpolate between for visual variation - this.shape = blobs2.svgPath({ + this.shape = svgPath({ seed: Math.random(), extraPoints: 6, randomness: 6, size: this.size, }); - this.shape2 = blobs2.svgPath({ + this.shape2 = svgPath({ seed: Math.random(), extraPoints: 8, randomness: 8, @@ -200,7 +200,9 @@ class NCBlob { this.initialize(ctx); } - if (!this.interpolator) return; + if (!this.interpolator) { + return; + } if (!this.gradient?.[0] || !this.gradient[1]) { return; @@ -247,9 +249,9 @@ const BackgroundBlobs = memo( }: BackgroundBlobsProps) => { const blobs = useMemo( () => [ - new Array(large).fill(null).map(() => new NCBlob(3, speedFactor)), - new Array(medium).fill(null).map(() => new NCBlob(2, speedFactor)), - new Array(small).fill(null).map(() => new NCBlob(1, speedFactor)), + new Array(large).fill(null).map(() => new NcBlob(3, speedFactor)), + new Array(medium).fill(null).map(() => new NcBlob(2, speedFactor)), + new Array(small).fill(null).map(() => new NcBlob(1, speedFactor)), ], [large, medium, small, speedFactor], ); diff --git a/packages/shared-consts/src/index.ts b/packages/shared-consts/src/index.ts deleted file mode 100644 index 15bd12e4..00000000 --- a/packages/shared-consts/src/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./assets"; -export * from "./codebook"; -export * from "./colors"; -export * from "./controls"; -export * from "./export-process"; -export * from "./network"; -export * from "./protocol"; -export * from "./session"; -export * from "./stages"; -export * from "./variables"; diff --git a/packages/ui/src/Alert.tsx b/packages/ui/src/Alert.tsx index 5fff669c..7590757c 100644 --- a/packages/ui/src/Alert.tsx +++ b/packages/ui/src/Alert.tsx @@ -43,4 +43,4 @@ const AlertDescription = React.forwardRef import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; -import * as React from "react"; import type { VariantProps } from "class-variance-authority"; +import { forwardRef, type ComponentPropsWithoutRef, type ElementRef, type HTMLAttributes } from "react"; import { buttonVariants } from "./Button"; import { DialogDescription, DialogTitle } from "./dialog"; import { cn } from "./utils"; @@ -17,9 +18,9 @@ const AlertDialogPortal = ({ ...props }: AlertDialogPrimitive.AlertDialogPortalP ); AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName; -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const AlertDialogOverlay = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const AlertDialogContent = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( @@ -50,19 +51,19 @@ const AlertDialogContent = React.forwardRef< )); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; -const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +const AlertDialogHeader = ({ className, ...props }: HTMLAttributes) => ( ); AlertDialogHeader.displayName = "AlertDialogHeader"; -const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +const AlertDialogFooter = ({ className, ...props }: HTMLAttributes) => ( ); AlertDialogFooter.displayName = "AlertDialogFooter"; -const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { +const AlertDialogAction = forwardRef< + ElementRef, + ComponentPropsWithoutRef & { buttonVariant?: VariantProps["variant"]; } >(({ className, buttonVariant, ...props }, ref) => ( @@ -74,9 +75,9 @@ const AlertDialogAction = React.forwardRef< )); AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; -const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { +const AlertDialogCancel = forwardRef< + ElementRef, + ComponentPropsWithoutRef & { buttonVariant?: VariantProps["variant"]; } >(({ className, buttonVariant, ...props }, ref) => ( @@ -94,12 +95,12 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { AlertDialog, - AlertDialogTrigger, + AlertDialogAction, + AlertDialogCancel, AlertDialogContent, - AlertDialogHeader, + DialogDescription as AlertDialogDescription, AlertDialogFooter, + AlertDialogHeader, DialogTitle as AlertDialogTitle, - DialogDescription as AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, + AlertDialogTrigger, }; diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button.tsx index 6be28a7f..1a926e61 100644 --- a/packages/ui/src/Button.tsx +++ b/packages/ui/src/Button.tsx @@ -1,8 +1,8 @@ import { Slot } from "@radix-ui/react-slot"; import type { VariantProps } from "class-variance-authority"; import { cva } from "class-variance-authority"; -import * as React from "react"; +import { type ButtonHTMLAttributes, forwardRef } from "react"; import { cn } from "./utils"; const baseButtonClasses = cn( @@ -50,9 +50,9 @@ export type ButtonProps = { variant?: VariantProps["variant"]; size?: VariantProps["size"]; asChild?: boolean; -} & React.ButtonHTMLAttributes; +} & ButtonHTMLAttributes; -const Button = React.forwardRef( +const Button = forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; return ; diff --git a/packages/ui/src/FancyBox.tsx b/packages/ui/src/FancyBox.tsx index d5bdf9ea..146677f3 100644 --- a/packages/ui/src/FancyBox.tsx +++ b/packages/ui/src/FancyBox.tsx @@ -51,10 +51,16 @@ export default function FancyBox { - if (value.length === 0) return placeholder; - if (value.length === items.length) return `All ${plural} Selected (${items.length})`; + if (value.length === 0) { + return placeholder; + } + if (value.length === items.length) { + return `All ${plural} Selected (${items.length})`; + } - if (value.length === 1) return `1 ${singular} Selected`; + if (value.length === 1) { + return `1 ${singular} Selected`; + } return `${value.length} ${plural} Selected`; }, [value, items, placeholder, plural, singular]); @@ -62,7 +68,7 @@ export default function FancyBox - + - + {showSearch && ( + {formattedDate} ); diff --git a/packages/ui/src/Input.tsx b/packages/ui/src/Input.tsx index 5b5987bd..e4371414 100644 --- a/packages/ui/src/Input.tsx +++ b/packages/ui/src/Input.tsx @@ -44,7 +44,7 @@ const Input = React.forwardRef( return ( {label && ( - + {label} )} diff --git a/packages/ui/src/accordion.tsx b/packages/ui/src/accordion.tsx index 6908f515..29efcbc2 100644 --- a/packages/ui/src/accordion.tsx +++ b/packages/ui/src/accordion.tsx @@ -51,4 +51,4 @@ const AccordionContent = React.forwardRef< AccordionContent.displayName = AccordionPrimitive.Content.displayName; -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }; diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx index c8085175..44a51786 100644 --- a/packages/ui/src/card.tsx +++ b/packages/ui/src/card.tsx @@ -39,4 +39,4 @@ const CardFooter = React.forwardRef(({ className, children, ...props }, ref) => ( - + {children} @@ -101,11 +101,11 @@ DialogClose.displayName = DialogPrimitive.Title.displayName; export { Dialog, - DialogTrigger, + DialogClose, DialogContent, - DialogHeader, + DialogDescription, DialogFooter, + DialogHeader, DialogTitle, - DialogDescription, - DialogClose, + DialogTrigger, }; diff --git a/packages/ui/src/dropdown-menu.tsx b/packages/ui/src/dropdown-menu.tsx index 9701bc04..74d799ef 100644 --- a/packages/ui/src/dropdown-menu.tsx +++ b/packages/ui/src/dropdown-menu.tsx @@ -164,18 +164,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { DropdownMenu, - DropdownMenuTrigger, + DropdownMenuCheckboxItem, DropdownMenuContent, + DropdownMenuGroup, DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenuTrigger, }; diff --git a/packages/ui/src/form.tsx b/packages/ui/src/form.tsx index 804d13c1..aecc5b9d 100644 --- a/packages/ui/src/form.tsx +++ b/packages/ui/src/form.tsx @@ -85,7 +85,7 @@ const FormLabel = React.forwardRef< >(({ className, ...props }, ref) => { const { error, formItemId } = useFormField(); - return ; + return ; }); FormLabel.displayName = "FormLabel"; @@ -97,7 +97,7 @@ const FormControl = React.forwardRef, React.Compon @@ -135,4 +135,4 @@ const FormMessage = React.forwardRef(({ className, children, ...props }, ref) => ( {children} - + @@ -109,4 +109,4 @@ const SelectSeparator = React.forwardRef< )); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; -export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator }; +export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue }; diff --git a/packages/ui/src/sheet.tsx b/packages/ui/src/sheet.tsx index f8c265e5..4b1e307a 100644 --- a/packages/ui/src/sheet.tsx +++ b/packages/ui/src/sheet.tsx @@ -97,13 +97,13 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName; export { Sheet, - SheetPortal, - SheetOverlay, - SheetTrigger, SheetClose, SheetContent, - SheetHeader, + SheetDescription, SheetFooter, + SheetHeader, + SheetOverlay, + SheetPortal, SheetTitle, - SheetDescription, + SheetTrigger, }; diff --git a/packages/ui/src/table.tsx b/packages/ui/src/table.tsx index 3f8d7c66..d0f421a2 100644 --- a/packages/ui/src/table.tsx +++ b/packages/ui/src/table.tsx @@ -72,4 +72,4 @@ const TableCaption = React.forwardRef - + diff --git a/packages/ui/src/toast.tsx b/packages/ui/src/toast.tsx index 0b7d90bb..4bc11941 100644 --- a/packages/ui/src/toast.tsx +++ b/packages/ui/src/toast.tsx @@ -1,15 +1,16 @@ +// biome-ignore lint/style/noNamespaceImport: radix import * as ToastPrimitives from "@radix-ui/react-toast"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { X } from "lucide-react"; -import * as React from "react"; +import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, type ReactElement } from "react"; import Heading from "./typography/Heading"; import { cn } from "./utils"; const ToastProvider = ToastPrimitives.Provider; -const ToastViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastViewport = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef & VariantProps +const Toast = forwardRef< + ElementRef, + ComponentPropsWithoutRef & VariantProps >(({ className, variant, ...props }, ref) => { return ; }); Toast.displayName = ToastPrimitives.Root.displayName; -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastAction = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const ToastClose = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const ToastTitle = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); ToastTitle.displayName = ToastPrimitives.Title.displayName; -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastDescription = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef; +type ToastProps = ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement; +type ToastActionElement = ReactElement; export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, Toast, - ToastTitle, - ToastDescription, - ToastClose, ToastAction, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, + type ToastActionElement, + type ToastProps, }; diff --git a/packages/ui/src/tooltip.tsx b/packages/ui/src/tooltip.tsx index 877b4ae2..f9fd09b5 100644 --- a/packages/ui/src/tooltip.tsx +++ b/packages/ui/src/tooltip.tsx @@ -1,7 +1,8 @@ "use client"; +// biome-ignore lint/style/noNamespaceImport: radix import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import * as React from "react"; +import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react"; import { cn } from "./utils"; const TooltipProvider = TooltipPrimitive.Provider; @@ -10,9 +11,9 @@ const Tooltip = TooltipPrimitive.Root; const TooltipTrigger = TooltipPrimitive.Trigger; -const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const TooltipContent = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, sideOffset = 4, ...props }, ref) => ( { ), }; } - case "REMOVE_TOAST": + case "REMOVE_TOAST": { if (action.toastId === undefined) { return { ...state, @@ -120,6 +118,10 @@ export const reducer = (state: State, action: Action): State => { ...state, toasts: state.toasts.filter((t) => t.id !== action.toastId), }; + } + + default: + return state; } }; @@ -153,7 +155,9 @@ function toast({ ...props }: Toast) { id, open: true, onOpenChange: (open) => { - if (!open) dismiss(); + if (!open) { + dismiss(); + } }, }, }); @@ -166,10 +170,10 @@ function toast({ ...props }: Toast) { } function useToast() { - const [state, setState] = React.useState(memoryState); + const [state, setState] = useState(memoryState); // biome-ignore lint/correctness/useExhaustiveDependencies: state is a reference, so it's fine - React.useEffect(() => { + useEffect(() => { listeners.push(setState); return () => { const index = listeners.indexOf(setState); @@ -186,4 +190,4 @@ function useToast() { }; } -export { useToast, toast }; +export { toast, useToast }; diff --git a/tooling/tailwind/base.ts b/tooling/tailwind/base.ts index 74a89ddd..4ee95b6f 100644 --- a/tooling/tailwind/base.ts +++ b/tooling/tailwind/base.ts @@ -1,5 +1,6 @@ import type { Config } from "tailwindcss"; +// biome-ignore lint/style/noDefaultExport: export default { darkMode: ["class"], content: ["./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"],
cur === locale); - if (!isValidLocale) notFound(); + if (!isValidLocale) { + notFound(); + } // setting setRequestLocale to support next-intl for static rendering setRequestLocale(locale); @@ -64,12 +65,12 @@ export default async function MainLayout({ children, params: { locale } }: MainL } return ( - + - + {children} - + diff --git a/apps/documentation/app/icon.svg b/apps/documentation/app/icon.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/app/icon.svg +++ b/apps/documentation/app/icon.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/app/not-found.tsx b/apps/documentation/app/not-found.tsx index 67fbc611..a6b7634a 100644 --- a/apps/documentation/app/not-found.tsx +++ b/apps/documentation/app/not-found.tsx @@ -13,7 +13,7 @@ const quicksand = Quicksand({ export default function NotFound() { return ( - + 404 - Not found diff --git a/apps/documentation/components/DocSearchComponent.tsx b/apps/documentation/components/DocSearchComponent.tsx index 87de1f36..cb842c65 100644 --- a/apps/documentation/components/DocSearchComponent.tsx +++ b/apps/documentation/components/DocSearchComponent.tsx @@ -1,10 +1,10 @@ "use client"; -import { DocSearch } from "@docsearch/react"; -import { useLocale, useTranslations } from "next-intl"; -import "@docsearch/css"; import { inputVariants } from "@codaco/ui"; +import "@docsearch/css"; +import { DocSearch } from "@docsearch/react"; import { Search } from "lucide-react"; +import { useLocale, useTranslations } from "next-intl"; import { env } from "~/env"; import { cn } from "~/lib/utils"; diff --git a/apps/documentation/components/Hero.tsx b/apps/documentation/components/Hero.tsx index 27a4b6a7..7ac9240a 100644 --- a/apps/documentation/components/Hero.tsx +++ b/apps/documentation/components/Hero.tsx @@ -58,7 +58,7 @@ export function Hero() { {t("Hero.title")} {t("Hero.tagline")} - + {resolvedTheme !== "dark" && ( diff --git a/apps/documentation/components/ProjectSwitcher.tsx b/apps/documentation/components/ProjectSwitcher.tsx index e614c5d6..94c8a9e1 100644 --- a/apps/documentation/components/ProjectSwitcher.tsx +++ b/apps/documentation/components/ProjectSwitcher.tsx @@ -68,7 +68,7 @@ export default function ProjectSwitcher() { {projects.map((p) => ( - + ))} diff --git a/apps/documentation/components/SharedNav/Menu.tsx b/apps/documentation/components/SharedNav/Menu.tsx index b17b6c3e..c0be451d 100644 --- a/apps/documentation/components/SharedNav/Menu.tsx +++ b/apps/documentation/components/SharedNav/Menu.tsx @@ -54,7 +54,7 @@ export const NavigationMenuDemo = () => { const t = useTranslations("SharedNavigation"); return ( - + {links.map((link, i) => { @@ -62,13 +62,13 @@ export const NavigationMenuDemo = () => { return ( - {t(link.translationKey)} + {t(link.translationKey)} {link.menu.map((subLink, i) => ( - + { const t = useTranslations("SharedNavigation"); const [submenu, setSubmenu] = useState([]); - if (!submenu.length) { + if (submenu.length === 0) { return ( {links.map((link, i) => { diff --git a/apps/documentation/components/Sidebar.tsx b/apps/documentation/components/Sidebar.tsx index 5917dbb2..a48aa40c 100644 --- a/apps/documentation/components/Sidebar.tsx +++ b/apps/documentation/components/Sidebar.tsx @@ -5,7 +5,7 @@ import { useLocale } from "next-intl"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { type RefObject, useEffect, useMemo, useRef, useState } from "react"; -import type { Locale, Project, SidebarPage, TSideBar, SidebarFolder as TSidebarFolder } from "~/app/types"; +import type { Locale, Project, SidebarPage, TSideBar, SidebarFolder as tSidebarFolder } from "~/app/types"; import { cn } from "~/lib/utils"; import sidebarData from "~/public/sidebar.json"; import DocSearchComponent from "./DocSearchComponent"; @@ -21,7 +21,7 @@ const MotionChevron = motion.create(ChevronRight); * @param sidebarItems {(TSidebarFolder | SidebarPage)[]} * @returns {(TSidebarFolder | SidebarPage)[]} */ -const sortSidebarItems = (sidebarItems: (TSidebarFolder | SidebarPage)[]) => +const sortSidebarItems = (sidebarItems: (tSidebarFolder | SidebarPage)[]) => sidebarItems.sort((a, b) => { // If both items have navOrder, sort by that if (a.navOrder !== null && b.navOrder !== null) { @@ -39,16 +39,20 @@ const sortSidebarItems = (sidebarItems: (TSidebarFolder | SidebarPage)[]) => return a.label.localeCompare(b.label); }); +const pathSeparatorRegex = /[\\/]/; + export function processSourceFile(type: "page", locale: Locale, sourceFile: string): string; export function processSourceFile(type: "folder", locale: Locale, sourceFile: string | undefined): string | undefined; // Used by sidebar to process sourceFile values into usable routes export function processSourceFile(type: "folder" | "page", locale: Locale, sourceFile?: string) { - if (!sourceFile) return; + if (!sourceFile) { + return; + } // We can't use path.sep because the webpack node shim always returns '/'. // Because this might be running on Windows, we need to use a regex to split // by either / or \. - const pathSegments = sourceFile.split(/[\\/]/).slice(2); + const pathSegments = sourceFile.split(pathSeparatorRegex).slice(2); let returnPath = ""; @@ -85,9 +89,13 @@ const SidebarFolder = ({ const pathname = usePathname(); const memoizedIsOpen = useMemo(() => { - if (alwaysOpen) return true; + if (alwaysOpen) { + return true; + } - if (defaultOpen) return true; + if (defaultOpen) { + return true; + } return (children as React.ReactElement<{ href?: string }>[]).some((child) => child.props.href === pathname); }, [alwaysOpen, defaultOpen, children, pathname]); @@ -103,7 +111,9 @@ const SidebarFolder = ({ defaultOpen={defaultOpen ?? alwaysOpen} open={isOpen} onOpenChange={() => { - if (alwaysOpen) return; + if (alwaysOpen) { + return; + } setIsOpen(!isOpen); }} className={cn("my-4 flex flex-col")} @@ -113,7 +123,7 @@ const SidebarFolder = ({ "focusable my-1 flex flex-1 items-center justify-between text-balance text-base font-semibold capitalize", !alwaysOpen && "cursor-pointer", )} - asChild + asChild={true} > {href ? ( @@ -123,7 +133,7 @@ const SidebarFolder = ({ className="h-4 w-4" initial={{ rotate: isOpen ? 90 : 0 }} animate={{ rotate: isOpen ? 90 : 0 }} - aria-hidden + aria-hidden={true} /> )} @@ -135,7 +145,7 @@ const SidebarFolder = ({ className="h-4 w-4" initial={{ rotate: isOpen ? 90 : 0 }} animate={{ rotate: isOpen ? 90 : 0 }} - aria-hidden + aria-hidden={true} /> )} @@ -143,7 +153,7 @@ const SidebarFolder = ({ @@ -192,7 +202,7 @@ const SidebarLink = ({ }; const renderSidebarItem = ( - item: TSidebarFolder | SidebarPage, + item: tSidebarFolder | SidebarPage, locale: Locale, sidebarContainerRef: RefObject, ) => { diff --git a/apps/documentation/components/TableOfContents.tsx b/apps/documentation/components/TableOfContents.tsx index b01c5711..36e5ae48 100644 --- a/apps/documentation/components/TableOfContents.tsx +++ b/apps/documentation/components/TableOfContents.tsx @@ -7,7 +7,7 @@ import useHighlighted from "~/hooks/useHighlighted"; import type { HeadingNode } from "~/lib/tableOfContents"; import { cn } from "~/lib/utils"; -const TOCLink = ({ +const tocLink = ({ node, sideBar, }: { @@ -18,7 +18,9 @@ const TOCLink = ({ const [highlighted] = useHighlighted(node.id); useEffect(() => { - if (!sideBar) return; + if (!sideBar) { + return; + } if (highlighted && ref.current) { ref.current.scrollIntoView({ @@ -73,7 +75,7 @@ function renderNodes(nodes: HeadingNode[], sideBar: boolean) { {nodes.map((node) => ( - + {node.children?.length > 0 && renderNodes(node.children, sideBar)} ))} diff --git a/apps/documentation/components/ai-assistant.tsx b/apps/documentation/components/ai-assistant.tsx index 9fdf8e2d..db4b644f 100644 --- a/apps/documentation/components/ai-assistant.tsx +++ b/apps/documentation/components/ai-assistant.tsx @@ -41,7 +41,7 @@ const TriggerButton = () => { return ( - + } {content} - {showToc && } + {showToc && } > ); } diff --git a/apps/documentation/components/customComponents/CodeCopyButton.tsx b/apps/documentation/components/customComponents/CodeCopyButton.tsx index 06d9a5b7..85fba26d 100644 --- a/apps/documentation/components/customComponents/CodeCopyButton.tsx +++ b/apps/documentation/components/customComponents/CodeCopyButton.tsx @@ -13,15 +13,13 @@ const CodeCopyButton = ({ code }: { code: string }) => { await navigator.clipboard.writeText(text); setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); // Reset state after 2 seconds - } catch (error) { - console.error("Failed to copy to clipboard:", error); - } + } catch (error) {} }; return ( - - + + {isCopied ? ( diff --git a/apps/documentation/components/customComponents/VideoIFrame.tsx b/apps/documentation/components/customComponents/VideoIFrame.tsx index 0a81e9da..01c4d5b5 100644 --- a/apps/documentation/components/customComponents/VideoIFrame.tsx +++ b/apps/documentation/components/customComponents/VideoIFrame.tsx @@ -1,5 +1,5 @@ const VideoIFrame = ({ src, title }: { src: string; title: string }) => { - return ; + return ; }; export default VideoIFrame; diff --git a/apps/documentation/lib/docs.tsx b/apps/documentation/lib/docs.tsx index 7479ac26..900cfde2 100644 --- a/apps/documentation/lib/docs.tsx +++ b/apps/documentation/lib/docs.tsx @@ -1,9 +1,10 @@ -import { existsSync, readFileSync } from "node:fs"; -import { join, sep } from "node:path"; import { Button, Details, Heading, ListItem, OrderedList, Paragraph, Summary, UnorderedList } from "@codaco/ui"; import rehypeFigure from "@microflash/rehype-figure"; import type { LinkProps } from "next/link"; +import { existsSync, readFileSync } from "node:fs"; +import { join, sep } from "node:path"; import type { ReactNode } from "react"; +// biome-ignore lint/style/noNamespaceImport: workaround import * as prod from "react/jsx-runtime"; import rehypeHighlight from "rehype-highlight"; import rehypeRaw from "rehype-raw"; @@ -32,7 +33,6 @@ import KeyConcept from "~/components/customComponents/KeyConcept"; import Pre from "~/components/customComponents/Pre"; import { PrerequisitesSection, SummaryCard, SummarySection } from "~/components/customComponents/SummaryCard"; import TipBox, { type TipBoxProps } from "~/components/customComponents/TipBox"; -import VideoIFrame from "~/components/customComponents/VideoIFrame"; import sidebar from "~/public/sidebar.json"; import { get } from "./helper_functions"; import processPreTags from "./processPreTags"; @@ -113,7 +113,6 @@ export const getDocsForRouteSegment = ({ const sidebarData = get(typedSidebar, [locale, project], null) as SidebarProject; if (!sidebarData) { - console.log(`No sidebar data found for ${locale} and ${project}`); return []; } @@ -159,7 +158,9 @@ export const getDocsForRouteSegment = ({ export const getSourceFile = (locale: string, project: string, pathSegment?: string[]) => { const projectSourceFile = get(sidebar, [locale, project, "sourceFile"], null) as string; - if (!pathSegment) return join(process.cwd(), projectSourceFile); + if (!pathSegment) { + return join(process.cwd(), projectSourceFile); + } const pathSegmentWithChildren = pathSegment.flatMap((segment, index) => { if (index === 0) { @@ -175,7 +176,9 @@ export const getSourceFile = (locale: string, project: string, pathSegment?: str null, ) as string | null; - if (!folderSourceFile) return null; + if (!folderSourceFile) { + return null; + } return join(process.cwd(), folderSourceFile); }; @@ -192,7 +195,6 @@ export async function getDocumentForPath({ const sourceFile = getSourceFile(locale, project, pathSegment); if (!sourceFile || (sourceFile && !existsSync(sourceFile))) { - console.log(`File not found: ${sourceFile}`); return null; } @@ -261,7 +263,7 @@ export async function getDocumentForPath({ keyconcept: (props: { title: string; children: ReactNode }) => , goodpractice: (props: { children: ReactNode }) => , badpractice: (props: { children: ReactNode }) => , - videoiframe: (props: { src: string; title: string }) => , + videoiframe: (props: { src: string; title: string }) => , summarycard: (props: { duration: string; children: ReactNode }) => , prerequisitessection: (props: { children: ReactNode }) => , summarysection: (props: { children: ReactNode }) => , diff --git a/apps/documentation/lib/helper_functions.ts b/apps/documentation/lib/helper_functions.ts index 5c896b6b..66f7ec1c 100644 --- a/apps/documentation/lib/helper_functions.ts +++ b/apps/documentation/lib/helper_functions.ts @@ -55,7 +55,9 @@ export const getNestedPath = (path: string) => { // it might not work for some edge cases. Test your code! export const get = (obj: Record, path: string | string[], defValue: unknown = undefined) => { // If path is not defined or it has false value - if (!path) return undefined; + if (!path) { + return undefined; + } // Check if path is string or array. Regex : ensure that we do not have '.' and brackets. // Regex explained: https://regexr.com/58j0k const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); diff --git a/apps/documentation/lib/processPreTags.ts b/apps/documentation/lib/processPreTags.ts index f2e12e6b..e22b2c39 100644 --- a/apps/documentation/lib/processPreTags.ts +++ b/apps/documentation/lib/processPreTags.ts @@ -18,7 +18,9 @@ const processPreTags = () => { return (tree: Tree) => { visit(tree, { tagName: "pre" }, (node) => { const codeElement = node.children.find((child) => child.tagName === "code"); - if (!codeElement) return; + if (!codeElement) { + return; + } // Extract the text content from the `code` element const rawCodeContent = hastNodeToString(codeElement); diff --git a/apps/documentation/lib/writeSidebarJson.ts b/apps/documentation/lib/writeSidebarJson.ts index a6f0d6ba..722685cb 100644 --- a/apps/documentation/lib/writeSidebarJson.ts +++ b/apps/documentation/lib/writeSidebarJson.ts @@ -60,16 +60,15 @@ function generateSidebarData() { } // Only process files ending in .md or .mdx - if (!file.name.endsWith(".md") && !file.name.endsWith(".mdx")) return; + if (!file.name.endsWith(".md") && !file.name.endsWith(".mdx")) { + return; + } // Determine locale based on file name (format is `index.en.mdx` or `index.en.md`) const locale = file.name.split(".")[1] as Locale | undefined; // If there's no locale, or the locale isn't included in the type, ignore it. if (!locale || !locales.includes(locale as Locale)) { - console.warn( - `File ${file.name} is missing a locale or has a locale not defined in Locale. Locale is ${locale}. Skipping.`, - ); return; } @@ -83,7 +82,9 @@ function generateSidebarData() { const matterResult = matter(markdownFile); // If file has "hidden: true" in frontmatter, skip it - if (matterResult.data.hidden) return; + if (matterResult.data.hidden) { + return; + } set(sidebarData[locale], [...nestedPath, key], createPageEntry(file, matterResult)); } @@ -96,5 +97,7 @@ try { writeFileSync(join(process.cwd(), "public", "sidebar.json"), JSON.stringify(sidebarData, null, 2), "utf-8"); } catch (e) { - console.log("Error writing sidebar data!", e); + // biome-ignore lint/suspicious/noConsole: + console.error(e); + process.exit(1); } diff --git a/apps/documentation/public/algolia-logo.svg b/apps/documentation/public/algolia-logo.svg index 44e9942d..2a5d5e63 100644 --- a/apps/documentation/public/algolia-logo.svg +++ b/apps/documentation/public/algolia-logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/logo-inline.svg b/apps/documentation/public/assets/img/logo-inline.svg index 9c3e33d8..d5058852 100644 --- a/apps/documentation/public/assets/img/logo-inline.svg +++ b/apps/documentation/public/assets/img/logo-inline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/logo.svg b/apps/documentation/public/assets/img/logo.svg index 0ad76d88..f8cbf280 100644 --- a/apps/documentation/public/assets/img/logo.svg +++ b/apps/documentation/public/assets/img/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/documentation/public/assets/img/sidebar-bg-cropped.svg b/apps/documentation/public/assets/img/sidebar-bg-cropped.svg index f84765cb..b015ae8e 100644 --- a/apps/documentation/public/assets/img/sidebar-bg-cropped.svg +++ b/apps/documentation/public/assets/img/sidebar-bg-cropped.svg @@ -1 +1 @@ -sidebar-bg-cropped \ No newline at end of file +sidebar-bg-cropped \ No newline at end of file diff --git a/apps/documentation/public/assets/img/tip-caution.svg b/apps/documentation/public/assets/img/tip-caution.svg index 49535f4b..dd2e7921 100644 --- a/apps/documentation/public/assets/img/tip-caution.svg +++ b/apps/documentation/public/assets/img/tip-caution.svg @@ -1 +1 @@ -Warning \ No newline at end of file +Warning \ No newline at end of file diff --git a/apps/documentation/public/assets/img/tip-info.svg b/apps/documentation/public/assets/img/tip-info.svg index c77c1b4c..e590d33d 100644 --- a/apps/documentation/public/assets/img/tip-info.svg +++ b/apps/documentation/public/assets/img/tip-info.svg @@ -1 +1 @@ -Info \ No newline at end of file +Info \ No newline at end of file diff --git a/apps/documentation/public/favicons/icon.svg b/apps/documentation/public/favicons/icon.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/public/favicons/icon.svg +++ b/apps/documentation/public/favicons/icon.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/public/images/key-concept.svg b/apps/documentation/public/images/key-concept.svg index 2d458866..a3db2199 100644 --- a/apps/documentation/public/images/key-concept.svg +++ b/apps/documentation/public/images/key-concept.svg @@ -6,6 +6,6 @@ .st0{fill:#DEA800;} .st1{fill:#F2B700;} - - + + diff --git a/apps/documentation/public/images/mark.svg b/apps/documentation/public/images/mark.svg index f19819df..52f50a5b 100644 --- a/apps/documentation/public/images/mark.svg +++ b/apps/documentation/public/images/mark.svg @@ -12,12 +12,12 @@ .st6{fill:#0FA3CF;} .st7{fill:#13B3E2;} - - - - - - - - + + + + + + + + diff --git a/apps/documentation/public/images/robot.svg b/apps/documentation/public/images/robot.svg index 4efe235a..54577b3f 100644 --- a/apps/documentation/public/images/robot.svg +++ b/apps/documentation/public/images/robot.svg @@ -28,56 +28,56 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/documentation/public/images/tip-caution.svg b/apps/documentation/public/images/tip-caution.svg index d5388560..43b64186 100644 --- a/apps/documentation/public/images/tip-caution.svg +++ b/apps/documentation/public/images/tip-caution.svg @@ -9,22 +9,22 @@ .st3{fill:#F22F66;} - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/apps/documentation/public/images/tip-info.svg b/apps/documentation/public/images/tip-info.svg index c77c1b4c..e590d33d 100644 --- a/apps/documentation/public/images/tip-info.svg +++ b/apps/documentation/public/images/tip-info.svg @@ -1 +1 @@ -Info \ No newline at end of file +Info \ No newline at end of file diff --git a/apps/documentation/public/images/typemark-negative.svg b/apps/documentation/public/images/typemark-negative.svg index 96a0954a..35c15fdd 100644 --- a/apps/documentation/public/images/typemark-negative.svg +++ b/apps/documentation/public/images/typemark-negative.svg @@ -15,34 +15,34 @@ - - + + - - + + - - + - - + - - - - - - - - - - - - - - + + - - + + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/biome.json b/biome.json index cd382fd6..3b90cb66 100644 --- a/biome.json +++ b/biome.json @@ -7,8 +7,33 @@ "enabled": true, "rules": { "recommended": true, + "performance": { + "all": true + }, + "security": { + "all": true + }, + "style": { + "all": true, + "useNamingConvention": "off", + "useFilenamingConvention": "off", + "noDefaultExport": "off" + }, + "suspicious": { + "all": true, + "noReactSpecificProps": "off" + }, "correctness": { "noUnusedImports": "error" + }, + "a11y": { + "all": true + }, + "nursery": { + "useGoogleFontDisplay": "error", + "noDocumentImportInPage": "error", + "noHeadElement": "error", + "noHeadImportInDocument": "error" } } }, diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts index 171619dd..f0bfd706 100644 --- a/packages/analytics/src/index.ts +++ b/packages/analytics/src/index.ts @@ -88,12 +88,8 @@ export const createRouteHandler = ({ // Check if analytics is disabled if (disableAnalytics) { + // biome-ignore lint/suspicious/noConsole: console.info("🛑 Analytics disabled. Payload not sent."); - try { - console.info("Payload:", "\n", JSON.stringify(incomingEvent, null, 2)); - } catch (e) { - console.error("Error stringifying payload:", e); - } return NextResponse.json({ message: "Analytics disabled" }, { status: 200 }); } @@ -102,14 +98,13 @@ export const createRouteHandler = ({ const trackableEvent = TrackableEventSchema.safeParse(incomingEvent); if (!trackableEvent.success) { - console.error("Invalid event:", trackableEvent.error); return NextResponse.json({ error: "Invalid event" }, { status: 400 }); } // We don't want failures in third party services to prevent us from // tracking analytics events, so we'll catch any errors and log them // and continue with an 'Unknown' country code. - let countryISOCode = "Unknown"; + let countryIsoCode = "Unknown"; try { const ip = await fetch("https://api64.ipify.org").then((res) => res.text()); @@ -120,18 +115,20 @@ export const createRouteHandler = ({ const geoData = (await fetch(`http://ip-api.com/json/${ip}`).then((res) => res.json())) as GeoData; if (geoData.status === "success") { - countryISOCode = geoData.countryCode; + countryIsoCode = geoData.countryCode; } else { throw new Error(geoData.message); } } catch (e) { - console.error("Geolocation failed:", e); + const error = ensureError(e); + // biome-ignore lint/suspicious/noConsole: + console.error(`Error fetching country code: ${error.message}`); } const analyticsEvent: analyticsEvent = { ...trackableEvent.data, installationId, - countryISOCode, + countryISOCode: countryIsoCode, }; // Forward to backend @@ -159,8 +156,6 @@ export const createRouteHandler = ({ if (response.status === 500) { error = "Analytics platform returned an internal server error. Please check the platform logs."; } - - console.info(`⚠️ Analytics platform rejected event: ${error}`); return Response.json( { error, @@ -168,11 +163,9 @@ export const createRouteHandler = ({ { status: 500 }, ); } - console.info("🚀 Analytics event sent to platform!"); return Response.json({ message: "Event forwarded successfully" }); } catch (e) { const error = ensureError(e); - console.info("🚫 Internal error with sending analytics event."); return Response.json({ error: `Error in analytics route handler: ${error.message}` }, { status: 500 }); } diff --git a/packages/analytics/src/utils.ts b/packages/analytics/src/utils.ts index d562793e..d49e6aa0 100644 --- a/packages/analytics/src/utils.ts +++ b/packages/analytics/src/utils.ts @@ -1,17 +1,23 @@ // Helper function that ensures that a value is an Error export function ensureError(value: unknown): Error { - if (!value) return new Error("No value was thrown"); + if (!value) { + return new Error("No value was thrown"); + } - if (value instanceof Error) return value; + if (value instanceof Error) { + return value; + } // Test if value inherits from Error - if (Object.prototype.isPrototypeOf.call(value, Error)) return value as Error & typeof value; + if (Object.prototype.isPrototypeOf.call(value, Error)) { + return value as Error & typeof value; + } let stringified = "[Unable to stringify the thrown value]"; try { stringified = JSON.stringify(value); } catch (e) { - console.error(e); + // Do nothing } const error = new Error(`This value was thrown as is, not through an Error: ${stringified}`); diff --git a/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx b/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx index f8b8eabe..59144ba9 100644 --- a/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx +++ b/packages/art/src/BackgroundBlobs/BackgroundBlobs.tsx @@ -1,6 +1,6 @@ "use client"; -import * as blobs2 from "blobs/v2"; +import { svgPath } from "blobs/v2"; import { interpolatePath } from "d3-interpolate-path"; import { memo, useMemo } from "react"; import Canvas from "./Canvas"; @@ -32,7 +32,7 @@ const gradients = [ const DEFAULT_SPEED_FACTOR = 1; -class NCBlob { +class NcBlob { layer: 1 | 2 | 3; speed: number; angle: number; @@ -168,14 +168,14 @@ class NCBlob { this.positionY = randomInt(0 - this.size / 2, this.canvasHeight - this.size / 2); // Create two random shapes to interpolate between for visual variation - this.shape = blobs2.svgPath({ + this.shape = svgPath({ seed: Math.random(), extraPoints: 6, randomness: 6, size: this.size, }); - this.shape2 = blobs2.svgPath({ + this.shape2 = svgPath({ seed: Math.random(), extraPoints: 8, randomness: 8, @@ -200,7 +200,9 @@ class NCBlob { this.initialize(ctx); } - if (!this.interpolator) return; + if (!this.interpolator) { + return; + } if (!this.gradient?.[0] || !this.gradient[1]) { return; @@ -247,9 +249,9 @@ const BackgroundBlobs = memo( }: BackgroundBlobsProps) => { const blobs = useMemo( () => [ - new Array(large).fill(null).map(() => new NCBlob(3, speedFactor)), - new Array(medium).fill(null).map(() => new NCBlob(2, speedFactor)), - new Array(small).fill(null).map(() => new NCBlob(1, speedFactor)), + new Array(large).fill(null).map(() => new NcBlob(3, speedFactor)), + new Array(medium).fill(null).map(() => new NcBlob(2, speedFactor)), + new Array(small).fill(null).map(() => new NcBlob(1, speedFactor)), ], [large, medium, small, speedFactor], ); diff --git a/packages/shared-consts/src/index.ts b/packages/shared-consts/src/index.ts deleted file mode 100644 index 15bd12e4..00000000 --- a/packages/shared-consts/src/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./assets"; -export * from "./codebook"; -export * from "./colors"; -export * from "./controls"; -export * from "./export-process"; -export * from "./network"; -export * from "./protocol"; -export * from "./session"; -export * from "./stages"; -export * from "./variables"; diff --git a/packages/ui/src/Alert.tsx b/packages/ui/src/Alert.tsx index 5fff669c..7590757c 100644 --- a/packages/ui/src/Alert.tsx +++ b/packages/ui/src/Alert.tsx @@ -43,4 +43,4 @@ const AlertDescription = React.forwardRef import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; -import * as React from "react"; import type { VariantProps } from "class-variance-authority"; +import { forwardRef, type ComponentPropsWithoutRef, type ElementRef, type HTMLAttributes } from "react"; import { buttonVariants } from "./Button"; import { DialogDescription, DialogTitle } from "./dialog"; import { cn } from "./utils"; @@ -17,9 +18,9 @@ const AlertDialogPortal = ({ ...props }: AlertDialogPrimitive.AlertDialogPortalP ); AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName; -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const AlertDialogOverlay = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const AlertDialogContent = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( @@ -50,19 +51,19 @@ const AlertDialogContent = React.forwardRef< )); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; -const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +const AlertDialogHeader = ({ className, ...props }: HTMLAttributes) => ( ); AlertDialogHeader.displayName = "AlertDialogHeader"; -const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +const AlertDialogFooter = ({ className, ...props }: HTMLAttributes) => ( ); AlertDialogFooter.displayName = "AlertDialogFooter"; -const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { +const AlertDialogAction = forwardRef< + ElementRef, + ComponentPropsWithoutRef & { buttonVariant?: VariantProps["variant"]; } >(({ className, buttonVariant, ...props }, ref) => ( @@ -74,9 +75,9 @@ const AlertDialogAction = React.forwardRef< )); AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; -const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { +const AlertDialogCancel = forwardRef< + ElementRef, + ComponentPropsWithoutRef & { buttonVariant?: VariantProps["variant"]; } >(({ className, buttonVariant, ...props }, ref) => ( @@ -94,12 +95,12 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { AlertDialog, - AlertDialogTrigger, + AlertDialogAction, + AlertDialogCancel, AlertDialogContent, - AlertDialogHeader, + DialogDescription as AlertDialogDescription, AlertDialogFooter, + AlertDialogHeader, DialogTitle as AlertDialogTitle, - DialogDescription as AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, + AlertDialogTrigger, }; diff --git a/packages/ui/src/Button.tsx b/packages/ui/src/Button.tsx index 6be28a7f..1a926e61 100644 --- a/packages/ui/src/Button.tsx +++ b/packages/ui/src/Button.tsx @@ -1,8 +1,8 @@ import { Slot } from "@radix-ui/react-slot"; import type { VariantProps } from "class-variance-authority"; import { cva } from "class-variance-authority"; -import * as React from "react"; +import { type ButtonHTMLAttributes, forwardRef } from "react"; import { cn } from "./utils"; const baseButtonClasses = cn( @@ -50,9 +50,9 @@ export type ButtonProps = { variant?: VariantProps["variant"]; size?: VariantProps["size"]; asChild?: boolean; -} & React.ButtonHTMLAttributes; +} & ButtonHTMLAttributes; -const Button = React.forwardRef( +const Button = forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; return ; diff --git a/packages/ui/src/FancyBox.tsx b/packages/ui/src/FancyBox.tsx index d5bdf9ea..146677f3 100644 --- a/packages/ui/src/FancyBox.tsx +++ b/packages/ui/src/FancyBox.tsx @@ -51,10 +51,16 @@ export default function FancyBox { - if (value.length === 0) return placeholder; - if (value.length === items.length) return `All ${plural} Selected (${items.length})`; + if (value.length === 0) { + return placeholder; + } + if (value.length === items.length) { + return `All ${plural} Selected (${items.length})`; + } - if (value.length === 1) return `1 ${singular} Selected`; + if (value.length === 1) { + return `1 ${singular} Selected`; + } return `${value.length} ${plural} Selected`; }, [value, items, placeholder, plural, singular]); @@ -62,7 +68,7 @@ export default function FancyBox - + - + {showSearch && ( + {formattedDate} ); diff --git a/packages/ui/src/Input.tsx b/packages/ui/src/Input.tsx index 5b5987bd..e4371414 100644 --- a/packages/ui/src/Input.tsx +++ b/packages/ui/src/Input.tsx @@ -44,7 +44,7 @@ const Input = React.forwardRef( return ( {label && ( - + {label} )} diff --git a/packages/ui/src/accordion.tsx b/packages/ui/src/accordion.tsx index 6908f515..29efcbc2 100644 --- a/packages/ui/src/accordion.tsx +++ b/packages/ui/src/accordion.tsx @@ -51,4 +51,4 @@ const AccordionContent = React.forwardRef< AccordionContent.displayName = AccordionPrimitive.Content.displayName; -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }; diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx index c8085175..44a51786 100644 --- a/packages/ui/src/card.tsx +++ b/packages/ui/src/card.tsx @@ -39,4 +39,4 @@ const CardFooter = React.forwardRef(({ className, children, ...props }, ref) => ( - + {children} @@ -101,11 +101,11 @@ DialogClose.displayName = DialogPrimitive.Title.displayName; export { Dialog, - DialogTrigger, + DialogClose, DialogContent, - DialogHeader, + DialogDescription, DialogFooter, + DialogHeader, DialogTitle, - DialogDescription, - DialogClose, + DialogTrigger, }; diff --git a/packages/ui/src/dropdown-menu.tsx b/packages/ui/src/dropdown-menu.tsx index 9701bc04..74d799ef 100644 --- a/packages/ui/src/dropdown-menu.tsx +++ b/packages/ui/src/dropdown-menu.tsx @@ -164,18 +164,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { DropdownMenu, - DropdownMenuTrigger, + DropdownMenuCheckboxItem, DropdownMenuContent, + DropdownMenuGroup, DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenuTrigger, }; diff --git a/packages/ui/src/form.tsx b/packages/ui/src/form.tsx index 804d13c1..aecc5b9d 100644 --- a/packages/ui/src/form.tsx +++ b/packages/ui/src/form.tsx @@ -85,7 +85,7 @@ const FormLabel = React.forwardRef< >(({ className, ...props }, ref) => { const { error, formItemId } = useFormField(); - return ; + return ; }); FormLabel.displayName = "FormLabel"; @@ -97,7 +97,7 @@ const FormControl = React.forwardRef, React.Compon @@ -135,4 +135,4 @@ const FormMessage = React.forwardRef(({ className, children, ...props }, ref) => ( {children} - + @@ -109,4 +109,4 @@ const SelectSeparator = React.forwardRef< )); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; -export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator }; +export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue }; diff --git a/packages/ui/src/sheet.tsx b/packages/ui/src/sheet.tsx index f8c265e5..4b1e307a 100644 --- a/packages/ui/src/sheet.tsx +++ b/packages/ui/src/sheet.tsx @@ -97,13 +97,13 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName; export { Sheet, - SheetPortal, - SheetOverlay, - SheetTrigger, SheetClose, SheetContent, - SheetHeader, + SheetDescription, SheetFooter, + SheetHeader, + SheetOverlay, + SheetPortal, SheetTitle, - SheetDescription, + SheetTrigger, }; diff --git a/packages/ui/src/table.tsx b/packages/ui/src/table.tsx index 3f8d7c66..d0f421a2 100644 --- a/packages/ui/src/table.tsx +++ b/packages/ui/src/table.tsx @@ -72,4 +72,4 @@ const TableCaption = React.forwardRef - + diff --git a/packages/ui/src/toast.tsx b/packages/ui/src/toast.tsx index 0b7d90bb..4bc11941 100644 --- a/packages/ui/src/toast.tsx +++ b/packages/ui/src/toast.tsx @@ -1,15 +1,16 @@ +// biome-ignore lint/style/noNamespaceImport: radix import * as ToastPrimitives from "@radix-ui/react-toast"; -import { type VariantProps, cva } from "class-variance-authority"; +import { cva, type VariantProps } from "class-variance-authority"; import { X } from "lucide-react"; -import * as React from "react"; +import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, type ReactElement } from "react"; import Heading from "./typography/Heading"; import { cn } from "./utils"; const ToastProvider = ToastPrimitives.Provider; -const ToastViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastViewport = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef & VariantProps +const Toast = forwardRef< + ElementRef, + ComponentPropsWithoutRef & VariantProps >(({ className, variant, ...props }, ref) => { return ; }); Toast.displayName = ToastPrimitives.Root.displayName; -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastAction = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const ToastClose = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( , - React.ComponentPropsWithoutRef +const ToastTitle = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); ToastTitle.displayName = ToastPrimitives.Title.displayName; -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const ToastDescription = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef; +type ToastProps = ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement; +type ToastActionElement = ReactElement; export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, Toast, - ToastTitle, - ToastDescription, - ToastClose, ToastAction, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, + type ToastActionElement, + type ToastProps, }; diff --git a/packages/ui/src/tooltip.tsx b/packages/ui/src/tooltip.tsx index 877b4ae2..f9fd09b5 100644 --- a/packages/ui/src/tooltip.tsx +++ b/packages/ui/src/tooltip.tsx @@ -1,7 +1,8 @@ "use client"; +// biome-ignore lint/style/noNamespaceImport: radix import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import * as React from "react"; +import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react"; import { cn } from "./utils"; const TooltipProvider = TooltipPrimitive.Provider; @@ -10,9 +11,9 @@ const Tooltip = TooltipPrimitive.Root; const TooltipTrigger = TooltipPrimitive.Trigger; -const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef +const TooltipContent = forwardRef< + ElementRef, + ComponentPropsWithoutRef >(({ className, sideOffset = 4, ...props }, ref) => ( { ), }; } - case "REMOVE_TOAST": + case "REMOVE_TOAST": { if (action.toastId === undefined) { return { ...state, @@ -120,6 +118,10 @@ export const reducer = (state: State, action: Action): State => { ...state, toasts: state.toasts.filter((t) => t.id !== action.toastId), }; + } + + default: + return state; } }; @@ -153,7 +155,9 @@ function toast({ ...props }: Toast) { id, open: true, onOpenChange: (open) => { - if (!open) dismiss(); + if (!open) { + dismiss(); + } }, }, }); @@ -166,10 +170,10 @@ function toast({ ...props }: Toast) { } function useToast() { - const [state, setState] = React.useState(memoryState); + const [state, setState] = useState(memoryState); // biome-ignore lint/correctness/useExhaustiveDependencies: state is a reference, so it's fine - React.useEffect(() => { + useEffect(() => { listeners.push(setState); return () => { const index = listeners.indexOf(setState); @@ -186,4 +190,4 @@ function useToast() { }; } -export { useToast, toast }; +export { toast, useToast }; diff --git a/tooling/tailwind/base.ts b/tooling/tailwind/base.ts index 74a89ddd..4ee95b6f 100644 --- a/tooling/tailwind/base.ts +++ b/tooling/tailwind/base.ts @@ -1,5 +1,6 @@ import type { Config } from "tailwindcss"; +// biome-ignore lint/style/noDefaultExport: export default { darkMode: ["class"], content: ["./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"],