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 = {