diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 86fed8bef..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": ["next/core-web-vitals", "next/typescript"], - "rules": { - "@typescript-eslint/no-explicit-any": "off", - "react-hooks/rules-of-hooks": "off", - "@next/next/no-img-element": "off", - "@typescript-eslint/no-empty-object-type": "off", - "@typescript-eslint/no-unused-vars": "off", - "react-hooks/exhaustive-deps": "off" - } -} \ No newline at end of file diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/ci.yml similarity index 76% rename from .github/workflows/linkcheck.yml rename to .github/workflows/ci.yml index 5e0583a0a..2eb367153 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: linkcheck +name: CI on: push: @@ -23,10 +23,13 @@ jobs: - name: Install dependencies run: bun install --frozen-lockfile + - name: Run Biome checks + run: bun run biome check app/ components/ lib/ scripts/ content/ --error-on-warnings + - name: Generate llms.txt files run: bun run generate-llms env: LLMS_BASE_URL: https://docs.hiro.so - name: Run link validation - run: bun run lint + run: bun run validate-links diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..5c2ba504c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + } +} diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 000000000..a2aac229d --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,31 @@ +{ + "format_on_save": "on", + "formatter": "language_server", + "languages": { + "JavaScript": { + "format_on_save": "on", + "formatter": "language_server", + "language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"] + }, + "TypeScript": { + "format_on_save": "on", + "formatter": "language_server", + "language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"] + }, + "TSX": { + "format_on_save": "on", + "formatter": "language_server", + "language_servers": ["biome", "!vtsls", "!typescript-language-server", "!eslint"] + }, + "JSON": { + "format_on_save": "on", + "formatter": "language_server", + "language_servers": ["biome", "..."] + }, + "Markdown": { + "format_on_save": "on", + "formatter": "language_server", + "remove_trailing_whitespace_on_save": true + } + } +} diff --git a/app/(docs)/[...slug]/page.tsx b/app/(docs)/[...slug]/page.tsx index 47a79157a..bce4398b3 100644 --- a/app/(docs)/[...slug]/page.tsx +++ b/app/(docs)/[...slug]/page.tsx @@ -1,58 +1,52 @@ -import fs from "fs/promises"; -import matter from "gray-matter"; -import { source } from "@/lib/source"; -import { notFound } from "next/navigation"; -import type { HeadingProps } from "@/types"; -import { getMDXComponents } from "@/components/mdx"; -import { API } from "@/components/reference/api-page"; -import { APIPage } from "@/components/openapi/api-page"; -import { Badge } from "@/components/ui/badge"; +import fs from 'node:fs/promises'; +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import matter from 'gray-matter'; +import * as lucideIcons from 'lucide-react'; +import { CheckIcon } from 'lucide-react'; +import { notFound } from 'next/navigation'; +import { Callout } from '@/components/callout'; +import { FeedbackWrapper } from '@/components/feedback/feedback-wrapper'; +import { InteractiveHeader, InteractiveLayout } from '@/components/layouts/interactive'; import { DocsPage, - DocsPageLayout, - DocsPageHeader, + DocsPageBreadcrumb, DocsPageContent, DocsPageContentWrapper, - DocsPageBreadcrumb, - DocsPageTitle, DocsPageDescription, + DocsPageHeader, + DocsPageLayout, DocsPageProse, -} from "@/components/layouts/page"; -import { - InteractiveHeader, - InteractiveLayout, -} from "@/components/layouts/interactive"; -import defaultMdxComponents from "fumadocs-ui/mdx"; -import { LLMShare } from "@/components/llm-share"; -import { CheckIcon } from "lucide-react"; -import { TagFilterSystem } from "@/components/ui/tag-filter-system"; -import { getAllFilterablePages } from "@/lib/source"; -import { Mermaid } from "@/components/mdx/mermaid"; -import { Callout } from "@/components/callout"; -import * as customIcons from "@/components/ui/icon"; -import * as lucideIcons from "lucide-react"; -import { FeedbackWrapper } from "@/components/feedback/feedback-wrapper"; + DocsPageTitle, +} from '@/components/layouts/page'; +import { LLMShare } from '@/components/llm-share'; +import { getMDXComponents } from '@/components/mdx'; +import { Mermaid } from '@/components/mdx/mermaid'; +import { APIPage } from '@/components/openapi/api-page'; +import { API } from '@/components/reference/api-page'; +import { Badge } from '@/components/ui/badge'; +import * as customIcons from '@/components/ui/icon'; +import { TagFilterSystem } from '@/components/ui/tag-filter-system'; +import { getAllFilterablePages, source } from '@/lib/source'; +import type { HeadingProps } from '@/types'; -export default async function Page(props: { - params: Promise<{ slug?: string[] }>; -}) { +export default async function Page(props: { params: Promise<{ slug?: string[] }> }) { const params = await props.params; const page = source.getPage(params.slug); if (!page) notFound(); - const fileContent = await fs.readFile(page.data._file.absolutePath, "utf-8"); + const fileContent = await fs.readFile(page.data._file.absolutePath, 'utf-8'); const { content: rawMarkdownContent } = matter(fileContent); const LLMContent = rawMarkdownContent - .split("\n") - .filter((line) => !line.trim().startsWith("import")) - .join("\n") + .split('\n') + .filter((line) => !line.trim().startsWith('import')) + .join('\n') .trim(); const MDX = page.data.body; if (!MDX) { - console.error("MDX component is undefined for page:", page.url); + console.error('MDX component is undefined for page:', page.url); notFound(); } @@ -60,21 +54,21 @@ export default async function Page(props: { const allFilterablePages = getAllFilterablePages(); // Extract section from current page URL for scoped filtering - const currentSection = page.url.split("/").filter(Boolean)[1] || "general"; + const currentSection = page.url.split('/').filter(Boolean)[1] || 'general'; // Helper function to get icon component const getIconComponent = (iconName: string) => { - const iconProps = { className: "w-3 h-3 shrink-0" }; + const iconProps = { className: 'w-3 h-3 shrink-0' }; // Check custom icons first const CustomIcon = (customIcons as any)[iconName]; - if (CustomIcon && typeof CustomIcon === "function") { + if (CustomIcon && typeof CustomIcon === 'function') { return ; } // Try with "Icon" suffix for custom icons const CustomIconWithSuffix = (customIcons as any)[`${iconName}Icon`]; - if (CustomIconWithSuffix && typeof CustomIconWithSuffix === "function") { + if (CustomIconWithSuffix && typeof CustomIconWithSuffix === 'function') { return ; } @@ -88,7 +82,7 @@ export default async function Page(props: { for (const pattern of lucidePatterns) { const LucideIcon = (lucideIcons as any)[pattern]; - if (LucideIcon && typeof LucideIcon === "function") { + if (LucideIcon && typeof LucideIcon === 'function') { return ; } } @@ -140,10 +134,8 @@ export default async function Page(props: { /> ), h1: ({ children, ...props }: HeadingProps) => { - const H1 = - defaultMdxComponents.h1 as React.ComponentType; - const id = - typeof children === "string" ? children : undefined; + const H1 = defaultMdxComponents.h1 as React.ComponentType; + const id = typeof children === 'string' ? children : undefined; return (

{children} @@ -158,28 +150,21 @@ export default async function Page(props: { /> ), hr: (props: React.PropsWithChildren) => ( -
+
), - input: ( - props: React.InputHTMLAttributes, - ) => { - if (props.type === "checkbox") { + input: (props: React.InputHTMLAttributes) => { + if (props.type === 'checkbox') { return (
- {props.checked && ( - - )} + {props.checked && }
); @@ -191,10 +176,7 @@ export default async function Page(props: { />
- + @@ -243,10 +225,8 @@ export default async function Page(props: { /> ), h1: ({ children, ...props }: HeadingProps) => { - const H1 = - defaultMdxComponents.h1 as React.ComponentType; - const id = - typeof children === "string" ? children : undefined; + const H1 = defaultMdxComponents.h1 as React.ComponentType; + const id = typeof children === 'string' ? children : undefined; return (

{children} @@ -261,28 +241,21 @@ export default async function Page(props: { /> ), hr: (props: React.PropsWithChildren) => ( -
+
), - input: ( - props: React.InputHTMLAttributes, - ) => { - if (props.type === "checkbox") { + input: (props: React.InputHTMLAttributes) => { + if (props.type === 'checkbox') { return (
- {props.checked && ( - - )} + {props.checked && }
); @@ -294,10 +267,7 @@ export default async function Page(props: { />
- + @@ -314,9 +284,7 @@ export async function generateStaticParams() { ); } -export async function generateMetadata(props: { - params: Promise<{ slug?: string[] }>; -}) { +export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) { const params = await props.params; const page = source.getPage(params.slug); if (!page) notFound(); diff --git a/app/(docs)/error.tsx b/app/(docs)/error.tsx index 1930ed80b..39a19cc5b 100644 --- a/app/(docs)/error.tsx +++ b/app/(docs)/error.tsx @@ -1,9 +1,9 @@ -"use client"; +'use client'; -import React from "react"; -import Link from "next/link"; +import Link from 'next/link'; +import React from 'react'; -export default function Error({ +export default function ErrorBoundary({ error, reset, }: { @@ -12,31 +12,31 @@ export default function Error({ }) { React.useEffect(() => { // Hide sidebar for errors (including 404s) - const aside = document.querySelector("aside"); + const aside = document.querySelector('aside'); if (aside) { - aside.style.display = "none"; + aside.style.display = 'none'; } return () => { - const asideCleanup = document.querySelector("aside"); + const asideCleanup = document.querySelector('aside'); if (asideCleanup) { - asideCleanup.style.display = ""; + asideCleanup.style.display = ''; } }; }, []); // Check if this is likely a 404 error - const is404 = error.message?.includes("404") || error.message?.includes("not found"); + const is404 = error.message?.includes('404') || error.message?.includes('not found'); return (

- {is404 ? "Page not found" : "Something went wrong"} + {is404 ? 'Page not found' : 'Something went wrong'}

{is404 ? "The documentation you are looking for doesn't exist or has been moved." - : "An error occurred while loading this page."} + : 'An error occurred while loading this page.'}

{!is404 && (
); -} \ No newline at end of file +} diff --git a/app/(docs)/layout.tsx b/app/(docs)/layout.tsx index 6058b6ff1..a5f895fb0 100644 --- a/app/(docs)/layout.tsx +++ b/app/(docs)/layout.tsx @@ -1,7 +1,7 @@ -import { DocsLayout } from "@/components/layouts/docs"; -import type { ReactNode } from "react"; -import { baseOptions } from "@/app/layout.config"; -import { source } from "@/lib/source"; +import type { ReactNode } from 'react'; +import { baseOptions } from '@/app/layout.config'; +import { DocsLayout } from '@/components/layouts/docs'; +import { source } from '@/lib/source'; export default function Layout({ children }: { children: ReactNode }) { return ( diff --git a/app/(docs)/not-found.tsx b/app/(docs)/not-found.tsx index 315b86fb6..8be677752 100644 --- a/app/(docs)/not-found.tsx +++ b/app/(docs)/not-found.tsx @@ -1,20 +1,21 @@ -import Link from "next/link"; +import Link from 'next/link'; export default function DocsNotFound() { return ( <> -