diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 74cae7d..7ad84d7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,14 +1,13 @@ { "recommendations": [ "DavidAnson.vscode-markdownlint", // markdown linting - "yzhang.markdown-all-in-one", // nicer markdown support "esbenp.prettier-vscode", // prettier plugin "dbaeumer.vscode-eslint", // eslint plugin "bradlc.vscode-tailwindcss", // hinting / autocompletion for tailwind "ban.spellright", // Spell check for docs "stripe.vscode-stripe", // stripe VSCode extension - "Prisma.prisma", // syntax|format|completion for prisma "rebornix.project-snippets", // Share useful snippets between collaborators - "inlang.vs-code-extension" // improved i18n DX + "inlang.vs-code-extension", + "biomejs.biome" // improved i18n DX ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index af05fc7..4e9d4e3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,9 @@ { "typescript.tsdk": "node_modules/typescript/lib", - "editor.formatOnSave": false, + "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "quickfix.biome": true, + "source.organizeImports.biome": true }, "typescript.preferences.importModuleSpecifier": "non-relative", "spellright.language": ["en"], diff --git a/web/.eslintignore b/web/.eslintignore deleted file mode 100644 index 8fe11da..0000000 --- a/web/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -**/node_modules -**/.next -**/public -packages/prisma/zod -apps/web/public/embed diff --git a/web/.eslintrc.js b/web/.eslintrc.js deleted file mode 100644 index ed87c90..0000000 --- a/web/.eslintrc.js +++ /dev/null @@ -1,95 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: [ - // "plugin:playwright/playwright-test", - "next", - // "next/core-web-vitals", - "plugin:prettier/recommended", - // "turbo", - // "plugin:you-dont-need-lodash-underscore/compatible-warn", - ], - plugins: ["unused-imports"], - parserOptions: { - tsconfigRootDir: __dirname, - project: ["./tsconfig.json"], - // project: ["./apps/*/tsconfig.json", "./packages/*/tsconfig.json"], - }, - settings: { - next: { - // rootDir: ["apps/*/", "packages/*/"], - rootDir: ["src"], - }, - }, - rules: { - "@next/next/no-img-element": "off", - "@next/next/no-html-link-for-pages": "off", - "jsx-a11y/role-supports-aria-props": "off", // @see https://github.com/vercel/next.js/issues/27989#issuecomment-897638654 - // "playwright/no-page-pause": "error", - "react/jsx-curly-brace-presence": [ - "error", - { props: "never", children: "never" }, - ], - "react/self-closing-comp": ["error", { component: true, html: true }], - "@typescript-eslint/no-unused-vars": [ - "warn", - { - vars: "all", - varsIgnorePattern: "^_", - args: "after-used", - argsIgnorePattern: "^_", - destructuredArrayIgnorePattern: "^_", - }, - ], - "unused-imports/no-unused-imports": "error", - "no-restricted-imports": [ - "error", - { - patterns: ["lodash"], - }, - ], - "prefer-template": "error", - }, - overrides: [ - { - files: ["*.ts", "*.tsx"], - extends: [ - "plugin:@typescript-eslint/recommended", - // "plugin:@calcom/eslint/recommended", - ], - plugins: [ - "@typescript-eslint", - // "@calcom/eslint" - ], - parser: "@typescript-eslint/parser", - rules: { - "@typescript-eslint/consistent-type-imports": [ - "error", - { - prefer: "type-imports", - // TODO: enable this once prettier supports it - // fixStyle: "inline-type-imports", - fixStyle: "separate-type-imports", - disallowTypeAnnotations: false, - }, - ], - }, - // overrides: [ - // { - // files: ["**/playwright/**/*.{tsx,ts}"], - // rules: { - // "@typescript-eslint/no-unused-vars": "off", - // "no-undef": "off", - // }, - // }, - // ], - }, - // { - // files: ["**/playwright/**/*.{js,jsx}"], - // rules: { - // "@typescript-eslint/no-unused-vars": "off", - // "no-undef": "off", - // }, - // }, - ], -}; diff --git a/web/biome.json b/web/biome.json new file mode 100644 index 0000000..8a54b26 --- /dev/null +++ b/web/biome.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.2/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + } +} \ No newline at end of file diff --git a/web/bun.lockb b/web/bun.lockb index cfc30ac..c5cc14e 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/package.json b/web/package.json index fe6e469..588cbb8 100644 --- a/web/package.json +++ b/web/package.json @@ -12,7 +12,8 @@ "migrate-production": "bun run migrate.mts", "migrate-local": "SSL=false LOCAL=true bun run migrate.mts", "db-up": "docker-compose up", - "db-dev": "bun run db-up && bun run migrate-local" + "db-dev": "bun run db-up && bun run migrate-local", + "lint:fix": "bunx @biomejs/biome lint --apply ./src" }, "dependencies": { "@algolia/autocomplete-core": "^1.13.0", @@ -105,26 +106,17 @@ "zustand": "^4.4.7" }, "devDependencies": { - "@trivago/prettier-plugin-sort-imports": "4.1.1", + "@biomejs/biome": "1.5.2", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", "autoprefixer": "^10.0.1", "concurrently": "^8.2.2", "dotenv": "^16.3.1", "drizzle-kit": "^0.20.6", "eslint": "8.34.0", - "eslint-config-next": "^14.0.4", - "eslint-config-prettier": "^8.6.0", - "eslint-config-turbo": "latest", - "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-unused-imports": "^3.0.0", "postcss": "^8", "postgres": "^3.4.3", - "prettier": "2.8.6", - "prettier-plugin-tailwindcss": "0.2.5", "sharp": "^0.33.1", "tailwindcss": "^3.3.0", "typescript": "^5" diff --git a/web/src/components/DeploymentDisplay.tsx b/web/src/components/DeploymentDisplay.tsx index 659bb08..631fbce 100644 --- a/web/src/components/DeploymentDisplay.tsx +++ b/web/src/components/DeploymentDisplay.tsx @@ -122,7 +122,7 @@ export function DeploymentDisplay({ here diff --git a/web/src/components/MachineList.tsx b/web/src/components/MachineList.tsx index 9f828b9..a747aa3 100644 --- a/web/src/components/MachineList.tsx +++ b/web/src/components/MachineList.tsx @@ -223,7 +223,7 @@ export const columns: ColumnDef[] = [ href={machine.endpoint.replace( "comfyui-api", "comfyui-app" - )} + )} rel="noreferrer" > Open ComfyUI diff --git a/web/src/components/Navbar.tsx b/web/src/components/Navbar.tsx index 47d539f..52be0c8 100644 --- a/web/src/components/Navbar.tsx +++ b/web/src/components/Navbar.tsx @@ -98,7 +98,7 @@ export function Navbar() { variant="outline" className="rounded-full aspect-square p-2" > - + diff --git a/web/src/components/RunDisplay.tsx b/web/src/components/RunDisplay.tsx index 48fd424..fa518ac 100644 --- a/web/src/components/RunDisplay.tsx +++ b/web/src/components/RunDisplay.tsx @@ -1,4 +1,3 @@ -import { LiveStatus } from "./LiveStatus"; import { RunInputs } from "@/components/RunInputs"; import { RunOutputs } from "@/components/RunOutputs"; import { Badge } from "@/components/ui/badge"; @@ -14,6 +13,7 @@ import { TableCell, TableRow } from "@/components/ui/table"; import { getDuration, getRelativeTime } from "@/lib/getRelativeTime"; import { type findAllRuns } from "@/server/findAllRuns"; import { Suspense } from "react"; +import { LiveStatus } from "./LiveStatus"; export async function RunDisplay({ run, diff --git a/web/src/components/SharePageSettings.tsx b/web/src/components/SharePageSettings.tsx index f6a292c..d43d9d9 100644 --- a/web/src/components/SharePageSettings.tsx +++ b/web/src/components/SharePageSettings.tsx @@ -1,6 +1,5 @@ "use client"; -import { useServerActionData } from "./useServerActionData"; import { ButtonAction } from "@/components/ButtonActionLoader"; import { UpdateModal } from "@/components/InsertModal"; import { LoadingPageWrapper } from "@/components/LoadingWrapper"; @@ -15,6 +14,7 @@ import { ExternalLink } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useState } from "react"; +import { useServerActionData } from "./useServerActionData"; export function SharePageSettings({ deployment_id, diff --git a/web/src/components/VersionSelect.tsx b/web/src/components/VersionSelect.tsx index b1067d7..0f391b6 100644 --- a/web/src/components/VersionSelect.tsx +++ b/web/src/components/VersionSelect.tsx @@ -598,7 +598,7 @@ export function ViewWorkflowDetailsButton({ {key} >( ) export function Feedback() { - let [submitted, setSubmitted] = useState(false) + const [submitted, setSubmitted] = useState(false) function onSubmit(event: React.FormEvent) { event.preventDefault() diff --git a/web/src/components/docs/Prose.tsx b/web/src/components/docs/Prose.tsx index 940b370..ec8f62d 100644 --- a/web/src/components/docs/Prose.tsx +++ b/web/src/components/docs/Prose.tsx @@ -8,7 +8,7 @@ export function Prose({ as?: T className?: string }) { - let Component = as ?? 'div' + const Component = as ?? 'div' return ( ) { } export function ThemeToggle() { - let { resolvedTheme, setTheme } = useTheme() - let otherTheme = resolvedTheme === 'dark' ? 'light' : 'dark' - let [mounted, setMounted] = useState(false) + const { resolvedTheme, setTheme } = useTheme() + const otherTheme = resolvedTheme === 'dark' ? 'light' : 'dark' + const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) diff --git a/web/src/lib/remToPx.ts b/web/src/lib/remToPx.ts index d3c3953..77e164b 100644 --- a/web/src/lib/remToPx.ts +++ b/web/src/lib/remToPx.ts @@ -1,8 +1,10 @@ export function remToPx(remValue: number) { - let rootFontSize = - typeof window === 'undefined' - ? 16 - : parseFloat(window.getComputedStyle(document.documentElement).fontSize) + const rootFontSize = + typeof window === "undefined" + ? 16 + : parseFloat( + window.getComputedStyle(document.documentElement).fontSize, + ); return remValue * rootFontSize } diff --git a/web/src/mdx/rehype.mjs b/web/src/mdx/rehype.mjs index 8219655..9760e50 100644 --- a/web/src/mdx/rehype.mjs +++ b/web/src/mdx/rehype.mjs @@ -27,13 +27,13 @@ function rehypeShiki() { visit(tree, "element", (node) => { if (node.tagName === "pre" && node.children[0]?.tagName === "code") { - let codeNode = node.children[0]; - let textNode = codeNode.children[0]; + const codeNode = node.children[0]; + const textNode = codeNode.children[0]; node.properties.code = textNode.value; if (node.properties.language) { - let tokens = highlighter.codeToThemedTokens( + const tokens = highlighter.codeToThemedTokens( textNode.value, node.properties.language ); @@ -53,7 +53,7 @@ function rehypeShiki() { function rehypeSlugify() { return (tree) => { - let slugify = slugifyWithCounter(); + const slugify = slugifyWithCounter(); visit(tree, "element", (node) => { if (node.tagName === "h2" && !node.properties.id) { node.properties.id = slugify(toString(node)); @@ -64,10 +64,10 @@ function rehypeSlugify() { function rehypeAddMDXExports(getExports) { return (tree) => { - let exports = Object.entries(getExports(tree)); + const exports = Object.entries(getExports(tree)); - for (let [name, value] of exports) { - for (let node of tree.children) { + for (const [name, value] of exports) { + for (const node of tree.children) { if ( node.type === "mdxjsEsm" && new RegExp(`export\\s+const\\s+${name}\\s*=`).test(node.value) @@ -76,7 +76,7 @@ function rehypeAddMDXExports(getExports) { } } - let exportStr = `export const ${name} = ${value}`; + const exportStr = `export const ${name} = ${value}`; tree.children.push({ type: "mdxjsEsm", @@ -93,9 +93,9 @@ function rehypeAddMDXExports(getExports) { } function getSections(node) { - let sections = []; + const sections = []; - for (let child of node.children ?? []) { + for (const child of node.children ?? []) { if (child.type === "element" && child.tagName === "h2") { sections.push(`{ title: ${JSON.stringify(toString(child))}, diff --git a/web/src/mdx/search.mjs b/web/src/mdx/search.mjs index c9dcf4c..398765d 100644 --- a/web/src/mdx/search.mjs +++ b/web/src/mdx/search.mjs @@ -31,9 +31,9 @@ function extractSections() { visit(tree, (node) => { if (node.type === "heading" || node.type === "paragraph") { - let content = toString(excludeObjectExpressions(node)); + const content = toString(excludeObjectExpressions(node)); if (node.type === "heading" && node.depth <= 2) { - let hash = node.depth === 1 ? null : slugify(content); + const hash = node.depth === 1 ? null : slugify(content); sections.push([content, hash, []]); } else { sections.at(-1)?.[2].push(content); @@ -45,7 +45,7 @@ function extractSections() { } export default function (nextConfig = {}) { - let cache = new Map(); + const cache = new Map(); return Object.assign({}, nextConfig, { webpack(config, options) { @@ -53,20 +53,20 @@ export default function (nextConfig = {}) { test: __filename, use: [ createLoader(function () { - let appDir = path.resolve("./src/app/(docs)/docs"); + const appDir = path.resolve("./src/app/(docs)/docs"); this.addContextDependency(appDir); - let files = glob.sync("**/*.mdx", { cwd: appDir }); - let data = files.map((file) => { + const files = glob.sync("**/*.mdx", { cwd: appDir }); + const data = files.map((file) => { let url = `/${file.replace(/(^|\/)page\.mdx$/, "")}`; - let mdx = fs.readFileSync(path.join(appDir, file), "utf8"); + const mdx = fs.readFileSync(path.join(appDir, file), "utf8"); let sections = []; if (cache.get(file)?.[0] === mdx) { sections = cache.get(file)[1]; } else { - let vfile = { value: mdx, sections }; + const vfile = { value: mdx, sections }; processor.runSync(processor.parse(vfile), vfile); cache.set(file, [mdx, sections]); } diff --git a/web/src/routes/authError.ts b/web/src/routes/authError.ts index 5118314..6db6db4 100644 --- a/web/src/routes/authError.ts +++ b/web/src/routes/authError.ts @@ -1,4 +1,6 @@ import type { ResponseConfig } from "@asteasolutions/zod-to-openapi"; + + import { z } from "@hono/zod-openapi"; export const authError = {