diff --git a/biome.jsonc b/biome.jsonc index 046c4cf..0e66c0c 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,6 +1,4 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.2/schema.json", - // Code formatting and organization suggestions "assist": { "enabled": true, @@ -33,92 +31,22 @@ "react": "all" }, "rules": { - "a11y": "error", - "complexity": "error", - "correctness": "error", - "nursery": "off", - "performance": "error", - "recommended": true, - "security": "error", + "preset": "all", + "correctness": { + // Not a Qwik project - this rule produces false positives on ordinary + // React closures. + "useQwikValidLexicalScope": "off" + }, "style": { - "recommended": true, - // Too strict - hardcoded strings acceptable in this project + // Hardcoded UI strings are acceptable in this example app; no i18n layer. "noJsxLiterals": "off" }, - "suspicious": "error" - } - }, - - // Override settings - "overrides": [ - { - "includes": ["**/*.tsx", "**/*.jsx"], - "linter": { - "rules": { - "performance": { - // Next.js specific rule - not applicable to Vite/React projects - "noImgElement": "off" - }, - "style": { - // Allow default exports in React components - "noDefaultExport": "off", - // Allow class components (converting to function components is a larger refactor) - "useReactFunctionComponents": "off" - }, - "suspicious": { - // React specific rule - not applicable to Vite/React projects - "noReactSpecificProps": "off" - } - } - } - }, - { - "includes": [ - "**/*.spec.ts", - "**/*.spec.tsx", - "**/*.test.ts", - "**/*.test.tsx" - ], - "linter": { - "rules": { - "complexity": { - // Test files can have longer functions with multiple test cases - "noExcessiveLinesPerFunction": "off" - }, - "style": { - // HTTP headers follow spec casing (Authorization, not authorization) - "useNamingConvention": "off" - }, - "suspicious": { - // Pact test executeTest callbacks are inherently async - "useAwait": "off" - } - } - } - }, - { - "includes": ["*.config.ts", "*.config.js"], - "linter": { - "rules": { - "style": { - // Config files conventionally use default exports - "noDefaultExport": "off" - } - } - } - }, - { - "includes": ["**/*.ts", "**/*.tsx"], - "linter": { - "rules": { - "correctness": { - // TypeScript handles this - "noUnresolvedImports": "off", - // False positive (we don't use Qwik) - "useQwikValidLexicalScope": "off" - } - } + "suspicious": { + // This is a React project - className/htmlFor are the correct props. + // The rule targets non-React JSX (e.g. Solid, Preact) where it would + // rewrite them to the HTML-standard names. + "noReactSpecificProps": "off" } } - ] + } } diff --git a/package-lock.json b/package-lock.json index 3e67898..5bc9812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "spectre.css": "^0.5.9" }, "devDependencies": { - "@biomejs/biome": "2.4.16", + "@biomejs/biome": "2.5.1", "@pact-foundation/pact": "16.5.0", "@testing-library/jest-dom": "6.9.1", "@types/node": "25.9.4", @@ -91,9 +91,9 @@ "license": "MIT" }, "node_modules/@biomejs/biome": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.16.tgz", - "integrity": "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.1.tgz", + "integrity": "sha512-IXWLCxKmae+rI7LOHS1B3EbVisQ6GRAWbhN9msa6KjNCyFWrvKZWR4oUdinaNssrV852OrSHuSPa95h1GPJc7Q==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -107,20 +107,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.16", - "@biomejs/cli-darwin-x64": "2.4.16", - "@biomejs/cli-linux-arm64": "2.4.16", - "@biomejs/cli-linux-arm64-musl": "2.4.16", - "@biomejs/cli-linux-x64": "2.4.16", - "@biomejs/cli-linux-x64-musl": "2.4.16", - "@biomejs/cli-win32-arm64": "2.4.16", - "@biomejs/cli-win32-x64": "2.4.16" + "@biomejs/cli-darwin-arm64": "2.5.1", + "@biomejs/cli-darwin-x64": "2.5.1", + "@biomejs/cli-linux-arm64": "2.5.1", + "@biomejs/cli-linux-arm64-musl": "2.5.1", + "@biomejs/cli-linux-x64": "2.5.1", + "@biomejs/cli-linux-x64-musl": "2.5.1", + "@biomejs/cli-win32-arm64": "2.5.1", + "@biomejs/cli-win32-x64": "2.5.1" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.16.tgz", - "integrity": "sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==", "cpu": [ "arm64" ], @@ -135,9 +135,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.16.tgz", - "integrity": "sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.1.tgz", + "integrity": "sha512-RgwTqPAM8g2tn1j+b5oRjF/DbSBX8a4gwojtuG9XuhfK7GgomvZ9+T+tqjXiVbjLEeGJOoL6VEk8mvRTVeSybw==", "cpu": [ "x64" ], @@ -152,9 +152,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.16.tgz", - "integrity": "sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.1.tgz", + "integrity": "sha512-yhV35CzZh38VyMvTEXi3JTjxZBs++oCKK9KG8vB6VI5+uvQvZNR3BFWEKKzuOmx9DJJj7sQpZ4LQJcmbGTs3+Q==", "cpu": [ "arm64" ], @@ -172,9 +172,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.16.tgz", - "integrity": "sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-WMcvMLgByyTqVxGlq918NBBYliq9FRR9GAQVETHb+VjGVqXCZFfHlZHC1FX4ibuYY/Hg6TJE3rHU0xVrdJXNRw==", "cpu": [ "arm64" ], @@ -192,9 +192,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.16.tgz", - "integrity": "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.1.tgz", + "integrity": "sha512-J/7uHSX7NfoYDI7HijAkd8lnQIOrRb2W7j3X+tw4R+N5ExvXGsyXFiGdQcfcxfOmNQmZVSQOCDk757fwpzqQcg==", "cpu": [ "x64" ], @@ -212,9 +212,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.16.tgz", - "integrity": "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-ANTowtlLmPYm5yeMckWY8Xzb9Ix+JJP3tgHR/n6xRj1VWyIzzWtfRfih9hv9VmClwadpBvZduISZIbBsIlYG3A==", "cpu": [ "x64" ], @@ -232,9 +232,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.16.tgz", - "integrity": "sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.1.tgz", + "integrity": "sha512-zgXnKNgWPC4iPF7Y1lR3STUeCUuZRpD6IiOrC7TZTlh0Lx6FiVUT05myuMQHQ9D+1cc7uyMldi4forE6lp0ivQ==", "cpu": [ "arm64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.16", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.16.tgz", - "integrity": "sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.1.tgz", + "integrity": "sha512-6uxpR9hvaglANkZemeSiN/FhYgkGasrEGn267eXIWvjrjJ2LhDlk251IhjVJq6MXzkV2/bcXwLwSroLyPtqRZg==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 23a1192..708a0ed 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "check:fix": "biome check --write --unsafe ." }, "devDependencies": { - "@biomejs/biome": "2.4.16", + "@biomejs/biome": "2.5.1", "@pact-foundation/pact": "16.5.0", "@testing-library/jest-dom": "6.9.1", "@types/node": "25.9.4", diff --git a/src/App.tsx b/src/App.tsx index 97e8b8b..3251460 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,11 @@ -import { useEffect, useId, useState } from "react"; +import { useCallback, useEffect, useId, useState } from "react"; import { Link } from "react-router-dom"; import "spectre.css/dist/spectre.min.css"; import "spectre.css/dist/spectre-icons.min.css"; import "spectre.css/dist/spectre-exp.min.css"; -import API from "./api.ts"; -import Heading from "./Heading.tsx"; -import Layout from "./Layout.tsx"; +import { api } from "./api.ts"; +import { Heading } from "./Heading.tsx"; +import { Layout } from "./Layout.tsx"; import type { Product } from "./types/index.ts"; interface ProductTableRowProps { @@ -53,7 +53,7 @@ function ProductTable(props: ProductTableProps) { ); } -function App() { +export function App() { const [loading, setLoading] = useState(true); const [searchText, setSearchText] = useState(""); const [products, setProducts] = useState([]); @@ -61,7 +61,8 @@ function App() { const inputId = useId(); useEffect(() => { - API.getAllProducts() + api + .getAllProducts() .then((r) => { setLoading(false); setProducts(r); @@ -83,12 +84,19 @@ function App() { ); }; - setVisibleProducts(searchText ? findProducts(searchText) : products); + let visible = products; + if (searchText) { + visible = findProducts(searchText); + } + setVisibleProducts(visible); }, [searchText, products]); - const onSearchTextChange = (e: React.ChangeEvent) => { - setSearchText(e.target.value); - }; + const onSearchTextChange = useCallback( + (e: React.ChangeEvent) => { + setSearchText(e.target.value); + }, + [], + ); return ( @@ -105,6 +113,7 @@ function App() { onChange={onSearchTextChange} /> + {/* biome-ignore lint/style/noTernary: idiomatic JSX conditional rendering with an else branch */} {loading ? (
) : ( @@ -113,5 +122,3 @@ function App() { ); } - -export default App; diff --git a/src/ErrorBoundary.tsx b/src/ErrorBoundary.tsx index 096c351..ea56688 100644 --- a/src/ErrorBoundary.tsx +++ b/src/ErrorBoundary.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from "react"; import React from "react"; -import Heading from "./Heading.tsx"; -import Layout from "./Layout.tsx"; +import { Heading } from "./Heading.tsx"; +import { Layout } from "./Layout.tsx"; interface ErrorBoundaryProps { children: ReactNode; @@ -11,7 +11,7 @@ interface ErrorBoundaryState { hasError: boolean; } -export default class ErrorBoundary extends React.Component< +export class ErrorBoundary extends React.Component< ErrorBoundaryProps, ErrorBoundaryState > { @@ -27,6 +27,7 @@ export default class ErrorBoundary extends React.Component<
+ {/* biome-ignore lint/performance/noImgElement: Vite/React project — next/image is not applicable */}

@@ -23,5 +23,3 @@ function Heading(props: HeadingProps) {

); } - -export default Heading; diff --git a/src/Layout.tsx b/src/Layout.tsx index 2527342..d33a4b1 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -4,7 +4,7 @@ interface LayoutProps { children: ReactNode; } -function Layout(props: LayoutProps) { +export function Layout(props: LayoutProps) { return (
@@ -13,5 +13,3 @@ function Layout(props: LayoutProps) {
); } - -export default Layout; diff --git a/src/ProductPage.tsx b/src/ProductPage.tsx index 48afc01..6e76cf9 100644 --- a/src/ProductPage.tsx +++ b/src/ProductPage.tsx @@ -3,12 +3,12 @@ import { useParams } from "react-router-dom"; import "spectre.css/dist/spectre.min.css"; import "spectre.css/dist/spectre-icons.min.css"; import "spectre.css/dist/spectre-exp.min.css"; -import API from "./api.ts"; -import Heading from "./Heading.tsx"; -import Layout from "./Layout.tsx"; +import { api } from "./api.ts"; +import { Heading } from "./Heading.tsx"; +import { Layout } from "./Layout.tsx"; import type { Product } from "./types/index.ts"; -function ProductPage() { +export function ProductPage() { const { id } = useParams<{ id: string }>(); const [loading, setLoading] = useState(true); const [product, setProduct] = useState>({ id }); @@ -16,7 +16,8 @@ function ProductPage() { useEffect(() => { if (id) { - API.getProduct(id) + api + .getProduct(id) .then((r) => { setLoading(false); setProduct(r); @@ -42,6 +43,7 @@ function ProductPage() { return ( + {/* biome-ignore lint/style/noTernary: idiomatic JSX conditional rendering with an else branch */} {loading ? (
); } - -export default ProductPage; diff --git a/src/api.pact.spec.ts b/src/api.pact.spec.ts index 47a49d3..2b12413 100644 --- a/src/api.pact.spec.ts +++ b/src/api.pact.spec.ts @@ -1,19 +1,18 @@ +// biome-ignore-all lint/complexity/noExcessiveLinesPerFunction: test suites group many related cases in one describe block import { MatchersV3, PactV3 } from "@pact-foundation/pact"; -import { API } from "./api.ts"; +import { Api } from "./api.ts"; const { eachLike, like } = MatchersV3; const Pact = PactV3; const mockProvider = new Pact({ consumer: "pactflow-example-consumer-webhookless", - provider: import.meta.env.PACT_PROVIDER - ? import.meta.env.PACT_PROVIDER - : "pactflow-example-provider", + provider: import.meta.env.PACT_PROVIDER || "pactflow-example-provider", }); describe("API Pact test", () => { describe("retrieving a product", () => { - test("ID 10 exists", async () => { + test("ID 10 exists", () => { // Arrange const expectedProduct = { id: "10", @@ -31,6 +30,7 @@ describe("API Pact test", () => { method: "GET", path: "/product/10", headers: { + // biome-ignore lint/style/useNamingConvention: HTTP header name follows RFC 7235 casing Authorization: like("Bearer 2019-01-14T11:34:18.045Z"), }, }) @@ -43,16 +43,15 @@ describe("API Pact test", () => { }); return mockProvider.executeTest(async (mockserver) => { // Act - const api = new API(mockserver.url); + const api = new Api(mockserver.url); const product = await api.getProduct("10"); // Assert - did we get the expected response expect(product).toStrictEqual(expectedProduct); - return; }); }); - test("product does not exist", async () => { + test("product does not exist", () => { // set up Pact interactions mockProvider @@ -62,6 +61,7 @@ describe("API Pact test", () => { method: "GET", path: "/product/11", headers: { + // biome-ignore lint/style/useNamingConvention: HTTP header name follows RFC 7235 casing Authorization: like("Bearer 2019-01-14T11:34:18.045Z"), }, }) @@ -69,18 +69,17 @@ describe("API Pact test", () => { status: 404, }); return mockProvider.executeTest(async (mockserver) => { - const api = new API(mockserver.url); + const api = new Api(mockserver.url); // make request to Pact mock server await expect(api.getProduct("11")).rejects.toThrow( "Request failed with status code 404", ); - return; }); }); }); describe("retrieving products", () => { - test("products exists", async () => { + test("products exists", () => { // set up Pact interactions const expectedProduct = { id: "10", @@ -95,6 +94,7 @@ describe("API Pact test", () => { method: "GET", path: "/products", headers: { + // biome-ignore lint/style/useNamingConvention: HTTP header name follows RFC 7235 casing Authorization: like("Bearer 2019-01-14T11:34:18.045Z"), }, }) @@ -106,14 +106,13 @@ describe("API Pact test", () => { body: eachLike(expectedProduct), }); return mockProvider.executeTest(async (mockserver) => { - const api = new API(mockserver.url); + const api = new Api(mockserver.url); // make request to Pact mock server const products = await api.getAllProducts(); // assert that we got the expected response expect(products).toStrictEqual([expectedProduct]); - return; }); }); }); diff --git a/src/api.ts b/src/api.ts index d9780de..3967195 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,38 +1,39 @@ -import axios from "axios"; +import axios, { type AxiosRequestConfig } from "axios"; import type { Product } from "./types/index.ts"; -export class API { - private readonly baseURL: string; +export class Api { + private readonly baseUrl: string; constructor(url?: string) { - this.baseURL = url || import.meta.env.VITE_API_BASE_URL || ""; + this.baseUrl = url || import.meta.env.VITE_API_BASE_URL || ""; } generateAuthToken(): string { return `Bearer ${new Date().toISOString()}`; } + private requestConfig(): AxiosRequestConfig { + return { + // biome-ignore lint/style/useNamingConvention: `baseURL` is axios's option name, not a project identifier + baseURL: this.baseUrl, + headers: { + // biome-ignore lint/style/useNamingConvention: HTTP header name follows RFC 7235 casing + Authorization: this.generateAuthToken(), + }, + }; + } + getAllProducts(): Promise { return axios - .get("/products", { - baseURL: this.baseURL, - headers: { - Authorization: this.generateAuthToken(), - }, - }) + .get("/products", this.requestConfig()) .then((r) => r.data); } getProduct(id: string): Promise { return axios - .get(`/product/${id}`, { - baseURL: this.baseURL, - headers: { - Authorization: this.generateAuthToken(), - }, - }) + .get(`/product/${id}`, this.requestConfig()) .then((r) => r.data); } } -export default new API(import.meta.env.VITE_API_BASE_URL); +export const api = new Api(import.meta.env.VITE_API_BASE_URL); diff --git a/src/index.tsx b/src/index.tsx index 539817e..6c1561d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,9 +1,9 @@ -import ReactDom from "react-dom/client"; +import { createRoot } from "react-dom/client"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import "./index.css"; -import App from "./App.tsx"; -import ErrorBoundary from "./ErrorBoundary.tsx"; -import ProductPage from "./ProductPage.tsx"; +import { App } from "./App.tsx"; +import { ErrorBoundary } from "./ErrorBoundary.tsx"; +import { ProductPage } from "./ProductPage.tsx"; const routing = ( @@ -22,6 +22,6 @@ const routing = ( const rootElement = document.getElementById("root"); if (rootElement) { - const root = ReactDom.createRoot(rootElement); + const root = createRoot(rootElement); root.render(routing); } diff --git a/vite-plugin-check-provider.ts b/vite-plugin-check-provider.ts index e435670..840f4ee 100644 --- a/vite-plugin-check-provider.ts +++ b/vite-plugin-check-provider.ts @@ -1,16 +1,18 @@ // biome-ignore-all lint/suspicious/noConsole: Dev plugin needs console output for user guidance // biome-ignore-all lint/correctness/noProcessGlobal: Vite plugins need access to process.env +// biome-ignore-all lint/style/noProcessEnv: Vite plugins read configuration from process.env import type { Plugin, ViteDevServer } from "vite"; const PROVIDER_REPO = "https://github.com/pactflow/example-provider"; const DEFAULT_PROVIDER_URL = "http://localhost:8080"; +const REQUEST_TIMEOUT_MS = 2000; async function checkProviderAvailability( url: string, ): Promise<{ available: boolean; error?: string }> { try { const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 2000); + const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); const response = await fetch(`${url}/health`, { signal: controller.signal, @@ -19,9 +21,13 @@ async function checkProviderAvailability( return { available: response.ok }; } catch (error) { + let errorMessage = "Unknown error"; + if (error instanceof Error) { + errorMessage = error.message; + } return { available: false, - error: error instanceof Error ? error.message : "Unknown error", + error: errorMessage, }; } } @@ -56,7 +62,9 @@ export function checkProviderPlugin(): Plugin { // Check on server start server.httpServer?.once("listening", async () => { - if (hasChecked) return; + if (hasChecked) { + return; + } hasChecked = true; const providerUrl = @@ -73,10 +81,10 @@ export function checkProviderPlugin(): Plugin { const result = await checkProviderAvailability(providerUrl); - if (!result.available) { - printProviderInstructions(providerUrl); - } else { + if (result.available) { console.log(`\nāœ… Provider available at ${providerUrl}\n`); + } else { + printProviderInstructions(providerUrl); } }); }, diff --git a/vite.config.ts b/vite.config.ts index dc12815..5d86217 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,7 @@ import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; import { checkProviderPlugin } from "./vite-plugin-check-provider.ts"; +// biome-ignore lint/style/noDefaultExport: Vite requires the config to be the module's default export export default defineConfig({ plugins: [react(), checkProviderPlugin()], server: { diff --git a/vitest.config.ts b/vitest.config.ts index edffde7..74be649 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,7 @@ import react from "@vitejs/plugin-react"; import { defineConfig } from "vitest/config"; +// biome-ignore lint/style/noDefaultExport: Vitest requires the config to be the module's default export export default defineConfig({ plugins: [react()], test: {